Changeset - e0b7e9dd5b54
[Not reviewed]
default
0 11 0
Nathan Brink (binki) - 15 years ago 2010-07-31 00:57:35
ohnobinki@ohnopublishing.net
File uploads are now supported using DISTREN_REQUEST_FILE_POST and friends.
11 files changed with 875 insertions and 20 deletions:
0 comments (0 inline, 0 general)
configure.ac
Show inline comments
 
@@ -39,25 +39,25 @@ AC_TYPE_UINT32_T
 
dnl selective compilation
 
dnl For now, this is only left for when the C-based client is
 
dnl reintroducded.
 
AC_ARG_ENABLE([server],
 
	[AS_HELP_STRING([--disable-server],[Don't build the distren server])],
 
	[enable_server=$enableval],
 
	[enable_server=yes])
 
AM_CONDITIONAL([ENABLE_SERVER],
 
	[test "x$enable_server" = "xyes"])
 

	
 
dnl package dependencies:
 

	
 
PKG_CHECK_MODULES([DISTLIBS], [libconfuse >= 2.5 libcurl libxml-2.0 liblist >= 2.3.1 libarchive >= 2.8.0])
 
PKG_CHECK_MODULES([DISTLIBS], [libconfuse >= 2.5 libcurl libxml-2.0 liblist >= 2.3.1 libarchive >= 2.8.0 libcrypto])
 
AX_LIB_MYSQL
 
AS_IF( [test "x${MYSQL_VERSION}" = "x"],
 
	[ AC_MSG_ERROR([I need mysql]) ] )
 

	
 
LIBS_save="$LIBS"
 
CFLAGS_save="$CFLAGS"
 
LIBS="$LIBS $DISTLIBS_LIBS"
 
CFLAGS="$CFLAGS $DISTLIBS_CFLAGS"
 
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <list.h>
 
	#include <stdio.h>
 
	], [printf(stderr, "%s", list_brag);
 
	return 0;])], [AC_DEFINE([HAVE_LIST_BRAG], [], [Define if liblist has list_brag.])], [])
src/client/distren.h
Show inline comments
 
@@ -27,28 +27,33 @@
 
#include <stddef.h> /* size_t */
 

	
 
typedef struct distren *distren_t;
 

	
 
typedef struct distren_job *distren_job_t;
 

	
 
/**
 
 * Initializes a libdistren handle.
 
 */
 
int distren_init(distren_t *handle);
 

	
 
/**
 
 Submits a file to distren.
 
 @param job must not refer to a valid job.
 
 @TODO create a stream-based interface
 
*/
 
 * Submits a file to distren.
 
 *
 
 * Blocks.
 
 *
 
 * @param handle distren handle.
 
 * @param job must not refer to a valid job.
 
 * @param filename path to a tarball representing a job.
 
 * @return 0 on success, 1 on error.
 
 */
 
int distren_submit_file(distren_t handle, distren_job_t *job, const char *filename);
 

	
 
/**
 
   Retrieve a job-ID that can be used to refer to the job after the client's process has recycled. Currently, this will be the only way to retrieve a job handle (a distren_job_t) and, consequently, the only way to retrieve a finished render.
 
   A job ID is representable using a character string. Hopefully, they will be kept at a reasonably small length. It should be made up of alphanumeric characters so as to be useful in most situations. 
 
   @param jobid This will be set to a string allocated by the specified malloc implementation
 
   @param job The distren_job_t from which to retrieve a jobid
 
*/
 
int distren_job_geturi(distren_job_t job, char **joburi);
 

	
 
/**
 
   Retrieves a tarball of a rendering's result.
src/client/libdistren.c
Show inline comments
 
@@ -20,24 +20,26 @@
 
/*
 
  Implementation of distren_* functions from distren.h excluding distren_job_* functions.
 
 */
 

	
 
#include "common/config.h"
 
#include "common/options.h"
 
#include "common/protocol.h"
 
#include "common/remoteio.h"
 
#include "common/request.h"
 

	
 
#include "libdistren.h"
 

	
 
#include <errno.h>
 
#include <libgen.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <string.h>
 

	
 
/**
 
 * Handle common cleanup actions for distren_init().
 
 */
 
static void distren_init_cleanup(distren_t distren);
 

	
 
int distren_init(distren_t *handle)
 
{
 
  int tmp;
 
@@ -100,27 +102,115 @@ int distren_init(distren_t *handle)
 

	
 
  return 0;
 
}
 

	
 
static void distren_init_cleanup(distren_t distren)
 
{
 
  if(distren->rem)
 
    remoteio_close(distren->rem);
 
  distren->rem = NULL;
 
  distren_free(distren);
 
}
 

	
 
/**
 
 * @todo Stub
 
 */
 
#ifdef _WIN32
 
#define FOPEN_BINARY "b"
 
#else
 
#define FOPEN_BINARY ""
 
#endif
 
int distren_submit_file(distren_t handle, distren_job_t *job, const char *filename)
 
{
 
  return 1;
 
  FILE *in;
 
  char buf[DISTREN_REQUEST_FILE_POST_DATA_LEN];
 
  size_t len;
 
  void *data;
 

	
 
  struct distren_request *req;
 
  struct distren_request_file_post data_header;
 
  distren_request_file_post_context_t post_context;
 

	
 
  /* for basename() to play with */
 
  char *my_filename;
 

	
 
  errno = 0;
 
  in = fopen(filename, "r" FOPEN_BINARY);
 
  if(!in)
 
    {
 
      perror("fopen");
 
      return 1;
 
    }
 

	
 
  fprintf(stderr, "info: Starting upload of %s...\n", filename);
 

	
 
  /* prepare the server for a file upload */
 
  my_filename = strdup(filename);
 
  distren_request_file_post_start(&req,
 
				  &data,
 
				  &post_context,
 
				  handle->file_post_id,
 
				  basename(my_filename));
 
  free(my_filename);
 
  distren_request_send(handle->rem, req, data);
 
  distren_request_free_with_data(req, data);
 
  handle->file_post_id ++;
 

	
 
  /* send file body */
 
  while(in
 
	&& !ferror(in)
 
	&& !feof(in)
 
	&& handle->rem)
 
    {
 
      len = fread(buf, 1, DISTREN_REQUEST_FILE_POST_DATA_LEN, in);
 
      if(len == 0)
 
	continue;
 
      distren_request_file_post(&req,
 
				&data_header,
 
				post_context,
 
				buf,
 
				len);
 
      remoteio_write(handle->rem, req, sizeof(struct distren_request));
 
      /* should we check of handle->rem is NULL or not here...? */
 
      remoteio_write(handle->rem, &data_header, sizeof(struct distren_request_file_post));
 
      remoteio_write(handle->rem, buf, len);
 
      distren_request_free(req);
 

	
 
      /* ensure we have no more than a megabyte waiting to be sent. */
 
      while(handle->rem
 
	    && remoteio_sendq_len(handle->rem) / 3 > (1024 * 1024 / DISTREN_REQUEST_FILE_POST_DATA_LEN))
 
	{
 
	  fprintf(stderr, "info: %d packets waiting to be sent...\n", remoteio_sendq_len(handle->rem) / 3);
 
	  multiio_poll(handle->multiio, -1);
 
	}
 
    }
 

	
 
  /*
 
   * declare the upload as finished, marking it as cancelled on I/O
 
   * error.
 
  */
 
  distren_request_file_post_finish(&req, &data, post_context, (!in || ferror(in)) ? 1 : 0);
 
  if(handle->rem)
 
    distren_request_send(handle->rem, req, data);
 
    distren_request_free_with_data(req, data);
 

	
 
  if(in)
 
    fclose(in);
 
  if(!handle->rem)
 
    return 1;
 

	
 
  /* let's block until the file's gone. */
 
  while(handle->rem
 
	&& remoteio_sendq_len(handle->rem))
 
    {
 
      fprintf(stderr, "info: %d packets waiting to be sent...\n", remoteio_sendq_len(handle->rem) / 3);
 
      multiio_poll(handle->multiio, -1);
 
    }
 
  fprintf(stderr, "info: %s successfully uploaded, as far as we know.\n", filename);
 

	
 
  return 0;
 
}
 

	
 
int distren_free(distren_t handle)
 
{
 
  if(handle->rem)
 
    remoteio_close(handle->rem);
 
  free(handle);
 
  return 0;
 
}
src/client/libdistren.h
Show inline comments
 
@@ -63,25 +63,29 @@ struct distren
 
   * we've passed through the server's hacky MOTD
 
   */
 
  short done_ad;
 

	
 
  /*
 
   * The servertype bitmask of the remote server.
 
   */
 
  uint32_t servertype;
 

	
 
  /* something on which to call multiio_poll() ;-) */
 
  multiio_context_t multiio;
 

	
 
  
 
  /*
 
   * The last-used per-connection DISTREN_REQUEST_FILE_POST
 
   * identifier.
 
   */
 
  uint32_t file_post_id;
 
};
 

	
 
struct distren_job
 
{
 
  char *joburi;
 
};
 

	
 
/*
 
  functions
 
*/
 

	
 
/**
src/common/protocol.h
Show inline comments
 
@@ -11,24 +11,25 @@
 
  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_PROTOCOL_H
 
#define DISTREN_PROTOCOL_H
 

	
 
#include <openssl/evp.h>
 
#include <stddef.h>
 
#include <stdint.h>
 

	
 
/**
 
 * Server types:
 
 *
 
 * It is assumed that any client has at least the functionality of a
 
 * normal client (i.e., accepts all of the REQUIRED requests). These
 
 * bitmask values add to the types of requests one may make to a
 
 * server. After first connecting to a server or receiving a
 
 * connection, a client shall send a DISTREN_REQUEST_VERSION packet
 
 * describing its version.
 
@@ -129,88 +130,160 @@ enum distren_request_type
 
    /**
 
     * Request that the receiver send a DISTREN_REQUEST_FRAME_STATUS
 
     * packet. No data because the sending party might not beforehand
 
     * know what frames/jobs the receiving server is managing.
 
     *
 
     * DATA: (none)
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_RENDER
 
     */
 
    DISTREN_REQUEST_STATUS_GET = 10,
 

	
 
    /**
 
     * Declare that a client is preparing to post a file using a
 
     * particular post-id and with a user-friendly filename.
 
     *
 
     * Yes, this is evil. Yes, this tries to replicate
 
     * well-established protocols like FTP and HTTP (which is
 
     * sometimes itself mistreated as FTP). Yes, this will be
 
     * buggy. Why is it needed? For my personal coding experience and
 
     * because I don't know off-hand of any libraries that allow a
 
     * file upload to be interleaved over an existing connection with
 
     * its own protocol. Over TCP? We aren't doing real-time streaming
 
     * or anything :-p.
 
     *
 
     * Possible future expansions: ``transparent'' zlib compression.
 
     *
 
     * DATA: struct distren_request_file_post_start
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_POST_START = 11,
 

	
 
    /**
 
     * Allow a client to upload a job tarball over a remoteio line.  A
 
     * client that wants to do this must take care not to overbuffer
 
     * his sendqueue so as to be able to respond to PING packets in
 
     * time. A server receiving such a message will want to write the
 
     * file as directly to disk as possible to avoid bagging a server
 
     * down in swap.
 
     * file as directly to disk as possible to avoid bogging the
 
     * server down in swap. This packet may only be sent if
 
     * DISTREN_REQUEST_FILE_POST_START has previously been sent.
 
     *
 
     * It is potential that if a server is DISTREN_SERVERTYPE_SUBMIT
 
     * but not also DISTREN_SERVERTYPE_DISTRIBUTE, that this request
 
     * would be relayed by the DISTREN_SERVERTYPE_SUBMIT server to a
 
     * DISTREN_SERVERTYPE_DISTRIBUTE server so that other clients can
 
     * obtain the file from the distribution server. In this case, the
 
     * file's URL is already known.
 
     *
 
     * Of course, having this sort of functionality at all is where
 
     * the nasty security issues start coming into play :-D.
 
     *
 
     * DATA: struct distren_request_file_post followed by a maximum of 131072 bytes (128kB).
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_POST = 11,
 
    DISTREN_REQUEST_FILE_POST = 12,
 

	
 
    /**
 
     * Marks a post-id's file as having completely uploaded. Provides
 
     * verification information so that the file's integrity may be
 
     * verified.
 
     *
 
     * DATA: struct distren_request_file_post_finish
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_POST_FINISH = 13,
 

	
 
    /**
 
     * Request information about obtaining a file (such as a
 
     * cURL-compatible URL) based on a distren file URL.
 
     *
 
     * DATA: struct distren_request_file_find
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE_FIND = 12,
 
    DISTREN_REQUEST_FILE_FIND = 14,
 

	
 
    /**
 
     * Provide information about obtaining a file (such as a URL).
 
     *
 
     * DATA: struct distren_request_file
 
     *
 
     * REQUIRED: DISTREN_SERVERTYPE_DISTRIBUTE
 
     */
 
    DISTREN_REQUEST_FILE = 13,
 
    DISTREN_REQUEST_FILE = 15,
 
  };
 

	
 
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;
 
};
 

	
 
#define DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN (32)
 
/**
 
 * A DISTREN_REQUEST_VERSION is started with a bitmask specification
 
 * of the DISTREN_SERVERTYPE_* values.
 
 */
 
struct distren_request_version
 
{
 
  uint32_t servertype;
 
  /* + 1 is for terminating NULL */
 
  char package_string[DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN + 1];
 
};
 

	
 
#define DISTREN_REQUEST_FILE_POST_NAME_LEN (64)
 
struct distren_request_file_post_start
 
{
 
  /**
 
   * Uniquely identify this file upload (per connection).
 
   */
 
  uint32_t post_id;
 
  /**
 
   * The user-friendly filename
 
   */
 
  char filename[DISTREN_REQUEST_FILE_POST_NAME_LEN];
 
};
 

	
 
#define DISTREN_REQUEST_FILE_POST_DATA_LEN (1024*128)
 
struct distren_request_file_post
 
{
 
  /**
 
   * Uniquely identify this file upload (per connection).
 
   */
 
  uint32_t post_id;
 
};
 

	
 
struct distren_request_file_post_finish
 
{
 
  /**
 
   * Uniquely identify this file upload (per connection).
 
   */
 
  uint32_t post_id;
 
  /**
 
   * Marks this upload as failed/cancelled.
 
   */
 
  uint32_t cancel;
 
  /**
 
   * An SHA1sum of the entire file. Only set if last_packet is
 
   * nonzero. Should be otherwise ignored.
 
   */
 
  unsigned char sha[EVP_MAX_MD_SIZE];
 
};
 

	
 
/**
 
 * initializes and allocates request
 
 */
 
int distren_request_new(struct distren_request **req, uint32_t len, enum distren_request_type type);
 

	
 
struct remoteio;
 
/**
 
   Takes a struct distren_request and its associated data, allocates
 
   a new block of data to hold the whole packet, and packets the req
 
   header and data together.
 

	
 
   @param rem A remoteio handle to ship this packet off to
src/common/request.c
Show inline comments
 
@@ -10,26 +10,30 @@
 
 *
 
 * 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/config.h"
 

	
 
#include "common/request.h"
 

	
 
#include "common/protocol.h"
 

	
 
#include <arpa/inet.h>
 
#include <openssl/evp.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);
 
}
 

	
 
int distren_request_version(struct distren_request **req, void **data, uint32_t servertype, const char *package_string)
 
{
 
  struct distren_request_version *version;
 
@@ -70,12 +74,239 @@ int distren_request_poing(struct distren
 
  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 0;
 
}
 

	
 
/*
 
 * file posting stuffs
 
 */
 
struct distren_request_file_post_context
 
{
 
  EVP_MD_CTX *digest_ctx;
 
  /* stored in network-byte order */
 
  uint32_t post_id;
 
};
 

	
 
/**
 
 * Helper for distren_request_file_post_start() and
 
 * distren_request_parse_file_post_start().
 
 */
 
static int _distren_request_file_post_context_new(distren_request_file_post_context_t *post_context,
 
						  uint32_t post_id)
 
{
 
  *post_context = malloc(sizeof(struct distren_request_file_post_context));
 
  if(!*post_context)
 
    return 1;
 

	
 
  (*post_context)->post_id = htonl(post_id);
 
  (*post_context)->digest_ctx = EVP_MD_CTX_create();
 
  EVP_DigestInit_ex((*post_context)->digest_ctx, EVP_sha1(), NULL);
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * Frees a post_context and sets the pointer to NULL.
 
 */
 
static int _distren_request_file_post_context_free(distren_request_file_post_context_t *post_context)
 
{
 
  if(!*post_context)
 
    return 0;
 

	
 
  EVP_MD_CTX_destroy((*post_context)->digest_ctx);
 
  free(*post_context);
 

	
 
  *post_context = NULL;
 

	
 
  return 0;
 
}
 

	
 
int distren_request_file_post_context_free(distren_request_file_post_context_t post_context)
 
{
 
  /*
 
   * for the ``public'' function, we don't need to have post_context
 
   * set to NULL. Thus, we have this wrapper around
 
   * _distren_request_file_post_context_free().
 
   */
 
  return _distren_request_file_post_context_free(&post_context);
 
}
 

	
 
int distren_request_file_post_start(struct distren_request **req,
 
				    void **data,
 
				    distren_request_file_post_context_t *post_context,
 
				    uint32_t post_id,
 
				    const char *filename)
 
{
 
  struct distren_request_file_post_start *file_post_start;
 

	
 
  distren_request_new(req, sizeof(struct distren_request_file_post_start), DISTREN_REQUEST_FILE_POST_START);
 
  file_post_start = malloc(sizeof(struct distren_request_file_post_start));
 
  _distren_request_file_post_context_new(post_context, post_id);
 
  if(!*req
 
     || !file_post_start)
 
    {
 
      if(*req)
 
	distren_request_free(*req);
 
      free(file_post_start);
 
      _distren_request_file_post_context_free(post_context);
 
      return 1;
 
    }
 

	
 
  file_post_start->post_id = (*post_context)->post_id;
 
  strncpy(file_post_start->filename, filename, DISTREN_REQUEST_FILE_POST_NAME_LEN);
 

	
 
  *data = file_post_start;
 
  return 0;
 
}
 

	
 
int distren_request_parse_file_post_start(struct distren_request *req,
 
					  void *data,
 
					  distren_request_file_post_context_t *post_context,
 
					  uint32_t *post_id,
 
					  char **filename)
 
{
 
  int tmp;
 
  struct distren_request_file_post_start *file_post_start;
 

	
 
  /* keep our promises ;-) */
 
  *post_context = NULL;
 
  *filename = NULL;
 

	
 
  if(req->len < sizeof(struct distren_request_file_post_start))
 
    return 1;
 

	
 
  file_post_start = data;
 

	
 
  tmp = _distren_request_file_post_context_new(post_context, file_post_start->post_id);
 
  *filename = malloc(DISTREN_REQUEST_FILE_POST_NAME_LEN + 1);
 
  if(tmp
 
     || !*filename)
 
    {
 
      _distren_request_file_post_context_free(post_context);
 
      free(*filename);
 
    }
 

	
 
  memcpy(*filename, file_post_start->filename, DISTREN_REQUEST_FILE_POST_NAME_LEN);
 
  (*filename)[DISTREN_REQUEST_FILE_POST_NAME_LEN] = '\0';
 

	
 
  *post_id = ntohl(file_post_start->post_id);
 

	
 
  return 0;
 
}
 

	
 
int distren_request_file_post(struct distren_request **req,
 
			      struct distren_request_file_post *data_header,
 
			      distren_request_file_post_context_t post_context,
 
			      const void *data,
 
			      size_t len)
 
{
 
  distren_request_new(req, sizeof(struct distren_request_file_post) + len, DISTREN_REQUEST_FILE_POST);
 
  if(!*req)
 
    return 1;
 

	
 
  data_header->post_id = post_context->post_id;
 

	
 
  EVP_DigestUpdate(post_context->digest_ctx, data, len);
 

	
 
  return 0;
 
}
 

	
 
int distren_request_parse_file_post(struct distren_request *req,
 
				    void *data,
 
				    uint32_t *post_id,
 
				    distren_request_parse_file_post_find_context_func_t find_context_func,
 
				    void *find_post_context_data,
 
				    void **file_data,
 
				    size_t *file_data_len)
 
{
 
  struct distren_request_file_post *file_post;
 
  distren_request_file_post_context_t post_context;
 

	
 
  if(req->type != DISTREN_REQUEST_FILE_POST
 
     || req->len < sizeof(struct distren_request_file_post))
 
    return 1;
 

	
 
  file_post = data;
 
  *post_id = ntohl(file_post->post_id);
 
  post_context = (*find_context_func)(*post_id, find_post_context_data);
 
  if(!post_context
 
     || *post_id != post_context->post_id)
 
    return 1;
 

	
 
  *file_data = data + sizeof(struct distren_request_file_post);
 
  *file_data_len = req->len - sizeof(struct distren_request_file_post);
 
  EVP_DigestUpdate(post_context->digest_ctx, *file_data, *file_data_len);
 

	
 
  return 0;
 
}
 

	
 
int distren_request_file_post_finish(struct distren_request **req,
 
				     void **data,
 
				     distren_request_file_post_context_t post_context,
 
				     uint32_t cancel)
 
{
 
  struct distren_request_file_post_finish *file_post_finish;
 

	
 
  distren_request_new(req, sizeof(struct distren_request_file_post_finish), DISTREN_REQUEST_FILE_POST_FINISH);
 
  file_post_finish = malloc(sizeof(struct distren_request_file_post_finish));
 
  if(!*req
 
     || !file_post_finish)
 
    {
 
      if(*req)
 
	distren_request_free(*req);
 
      free(file_post_finish);
 

	
 
      _distren_request_file_post_context_free(&post_context);
 
      return 1;
 
    }
 
  file_post_finish->post_id = post_context->post_id;
 
  file_post_finish->cancel = htonl(cancel);
 

	
 
  EVP_DigestFinal(post_context->digest_ctx, file_post_finish->sha, NULL);
 
  _distren_request_file_post_context_free(&post_context);
 

	
 
  *data = file_post_finish;
 
  return 0;
 
}
 

	
 
int distren_request_parse_file_post_finish(struct distren_request *req,
 
				    void *data,
 
				    uint32_t *post_id,
 
				    distren_request_parse_file_post_find_context_func_t find_context_func,
 
				    void *find_post_context_data)
 
{
 
  struct distren_request_file_post_finish *file_post_finish;
 
  distren_request_file_post_context_t post_context;
 

	
 
  unsigned int checksum_len;
 
  unsigned char checksum[EVP_MAX_MD_SIZE];
 

	
 
  if(req->type != DISTREN_REQUEST_FILE_POST_FINISH
 
     || req->len < sizeof(struct distren_request_file_post_finish))
 
    return 1;
 

	
 
  file_post_finish = data;
 
  *post_id = ntohl(file_post_finish->post_id);
 
  if(ntohl(file_post_finish->cancel))
 
    return 2;
 

	
 
  post_context = find_context_func(*post_id, find_post_context_data);
 
  if(!post_context)
 
    return 1;
 

	
 
  /* finish up checksumming */
 
  EVP_DigestFinal_ex(post_context->digest_ctx, checksum, &checksum_len);
 
  if(checksum_len > EVP_MAX_MD_SIZE)
 
    checksum_len = EVP_MAX_MD_SIZE;
 
  if(memcmp(checksum, file_post_finish->sha, checksum_len))
 
    return 3;
 

	
 
  return 0;
 
}
src/common/request.h
Show inline comments
 
@@ -13,24 +13,26 @@
 
 * 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
 

	
 
#include "common/protocol.h"
 

	
 
#include <openssl/sha.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);
 

	
 
/**
 
@@ -53,15 +55,161 @@ int distren_request_version(struct distr
 
 */
 
int distren_request_parse_version(struct distren_request *req, void *data, struct distren_request_version *version);
 

	
 
/**
 
 * 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);
 
int distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len);
 

	
 
struct distren_request_file_post_context;
 
typedef struct distren_request_file_post_context *distren_request_file_post_context_t;
 

	
 
/**
 
 * Frees a post_context for if a client is disconnected or an upload
 
 * otherwise interrupted.
 
 */
 
int distren_request_file_post_context_free(distren_request_file_post_context_t post_context);
 

	
 
/**
 
 * Prepares for a file upload and initializes a
 
 * distren_request_file_post_context_t.
 
 *
 
 * If you call this function and you never finish an upload, you
 
 * _must_ call distren_request_file_post_finish() on post_context at
 
 * least to free the post_context.
 
 *
 
 * @param req where to store the new distren_request pointer.
 
 * @param data where to store the new request's data pointer. 
 
 * @param post_context where the newly allocated distren_request_file_post_context_t's addrss should be stored.
 
 * @param post_id a number that uniquely identifies this upload for this connection.
 
 * @param filename a user-friendly filename for the file posting.
 
 */
 
int distren_request_file_post_start(struct distren_request **req,
 
				    void **data,
 
				    distren_request_file_post_context_t *post_context,
 
				    uint32_t post_id,
 
				    const char *filename);
 

	
 
/**
 
 * Parses a DISTREN_REQUEST_FILE_POST_START request.
 
 *
 
 * @param req the recieved request.
 
 * @param data the request's data.
 
 * @param post_context a post_context to be initialized for a continued download. Must be free()d with a call to distren_request_parse_file_post_finish() or distren_request_file_post_context_free(), however.
 
 * @param post_id will be set to the post_id contained in the packet.
 
 * @param filename will be made to point at the filename the client requested. *filename must be free()d.
 
 * @return 0 upon no problem, 1 if there's an error. If 1, then *filename should not be used (it will be set NULL, so no problem calling free() on it though).
 
 */
 
int distren_request_parse_file_post_start(struct distren_request *req,
 
					  void *data,
 
					  distren_request_file_post_context_t *post_context,
 
					  uint32_t *post_id,
 
					  char **filename);
 

	
 
/**
 
 * Initializes a DISTREN_REQUEST_FILE_POST packet's header.
 
 *
 
 * As file posting requires an acceptable chunk of memory, we don't
 
 * malloc() the data portion of the packet here to encourage reading
 
 * files in through static buffers.
 
 *
 
 * As a single file takes multiple DISTREN_REQUEST_FILE_POST packets
 
 * before it's completely uploaded, certain state information has to
 
 * persist between calls to distren_request_file_post(). This is what
 
 * the post_context argument is for. This pointer must be preserved
 
 * between calls to distren_request_file_post().
 
 *
 
 * @param req place to store the new req's pointer
 
 * @param data_header where the data's header should be stored
 
 * @param post_context what this points to must be preserved between calls to distren_request_file_post(), for keeping state.
 
FILE_POST_DATA_LEN.
 
 * @param data a hunk of the file being posted.
 
 * @param len the length of the data argument. This must be less than or equal to DISTREN_REQUEST_ */
 
int distren_request_file_post(struct distren_request **req,
 
			      struct distren_request_file_post *data_header,
 
			      distren_request_file_post_context_t post_context,
 
			      const void *data,
 
			      size_t len);
 

	
 
/**
 
 * A callback to find a distren_request_file_post_context_t for a
 
 * specified post_id.
 
 *
 
 * This exists because it is recognized that two servers may be
 
 * transfering more than one file at a time between eachother. At the
 
 * time that distren_request_parse_file_post() is called, the caller
 
 * doesn't know post_id and thus can't provide the proper
 
 * post_context. Yet distren_request_parse_file_post() needs the
 
 * post_context to get its job done ;-).
 
 *
 
 * @param post_id the post_id for which to find a distren_request_file_post_context_t
 
 * @param find_post_context_data the same find_post_context_data pointer passed to distren_request_parse_file_post() or distren_request_parse_file_post_finish().
 
 * @return NULL if the context could not be found or the context.
 
 */
 
typedef distren_request_file_post_context_t(*distren_request_parse_file_post_find_context_func_t)(uint32_t post_id, void *find_post_context_data);
 

	
 
/**
 
 * Parse a DISTREN_REQUEST_FILE_POST packet and update the
 
 * post_context.
 
 *
 
 * @param req the request header.
 
 * @param data the request data section.
 
 * @param post_id will be filled with the request's post_id.
 
 * @param find_context_func a function that can look up the necessary context_post given a post_id.
 
 * @param find_post_context_data the pointer to pass as context data to find_context_func
 
 * @param file_data will be set to the beginning of the file data portion of the packet. Do not free() this pointer.
 
 * @param file_data_len the number of bytes of file data that may be accessed at *file_data.
 
 * @return 0 on success and 1 on failure, in which case the client should be kicked and you should free the post_context with distren_request_file_post_context_free().
 
 */
 
int distren_request_parse_file_post(struct distren_request *req,
 
				    void *data,
 
				    uint32_t *post_id,
 
				    distren_request_parse_file_post_find_context_func_t find_context_func,
 
				    void *find_post_context_data,
 
				    void **file_data,
 
				    size_t *file_data_len);
 

	
 
/**
 
 * Construct a packet signalling that a file posting is finished.
 
 *
 
 * Frees the memory used to store the post_context. Must be called
 
 * once for every call to distren_request_file_post_start(),
 
 * regardless of whether or not you actually call
 
 * distren_request_send() on the result.
 
 *
 
 * @param req where to store the pointer of the newly allocated request
 
 * @param data where to store the pointer t othe newly allocated request data
 
 * @param post_context the post_context which will be used and then freed.
 
 * @param cancel 0 if the file was uploaded successfully, 1 if the upload is cancelled or failed. 
 
 */
 
int distren_request_file_post_finish(struct distren_request **req,
 
				     void **data,
 
				     distren_request_file_post_context_t post_context,
 
				     uint32_t cancel);
 

	
 
/**
 
 * Parses a DISTREN_REQUEST_FILE_POST_FINISH packet.
 
 *
 
 * You must still call distren_request_file_post_context_free() on the
 
 * post_context which you must find yourself. If you are a server, you
 
 * must keep a list of these contexts per-client so that when a client
 
 * exits, you may reclaim these contexts.
 
 *
 
 * @param req the request header
 
 * @param data the data section of the packet
 
 * @param post_id a place where the post_id will be set.
 
 * @param find_context_func a function pointer to let me find a post_context based on the post_id of the request.
 
 * @param find_post_context_data the pointer to pass to the find_context_func callback.
 
 * @return 0 for a good packet, 1 for a bad packet or if the post_context can't be found (post_id won't be valid), 2 if the request asks for the user to cancel the file upload (post_id will be valid), and 3 if the checksums don't match (post_id will be valid).
 
 */
 
int distren_request_parse_file_post_finish(struct distren_request *req,
 
				    void *data,
 
				    uint32_t *post_id,
 
				    distren_request_parse_file_post_find_context_func_t find_context_func,
 
				    void *find_post_context_data);
 
#endif /* _DISTREN_REQUEST_H */
src/server/distrend.c
Show inline comments
 
@@ -24,24 +24,25 @@
 

	
 
#include "distrenjob.h"
 
#include "listen.h"
 
#include "slavefuncs.h"
 
#include "mysql.h"
 

	
 
#include "common/asprintf.h"
 
#include "common/execio.h"
 
#include "common/options.h"
 
#include "common/protocol.h"
 
#include "common/request.h"
 

	
 
#include <arpa/inet.h>
 
#include <confuse.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <sys/stat.h>
 
#include <sys/types.h>
 
#include <time.h>
 
#include <unistd.h>
 

	
 
#include <libxml/encoding.h>
 
#include <libxml/parser.h>
 
#include <libxml/tree.h>
 
@@ -52,24 +53,30 @@
 
/* ******************* Structs ************************ */
 
struct general_info
 
{
 
  struct distrenjob head;
 
  distrend_mysql_conn_t conn;
 

	
 
  struct distrend_config *config;
 

	
 
  struct
 
  {
 
    /** general_info.xml */
 
    char *geninfo;
 
    /**
 
     * where to store in-progress uploads or things that should
 
     * otherwise be on the same filesystem as the rest of the datadir
 
     * so that it may be rename()d.
 
     */
 
    char *tmpdir;
 
    
 
  } files;
 

	
 
  int jobs_in_queue;
 
  unsigned int free_clients;
 
  unsigned int rendering_clients;
 
  unsigned int total_finished_jobs;
 
  unsigned int total_frames_rendered;
 
  unsigned int highest_jobnum;
 
  int hibernate;
 
  time_t timestamp;
 
  unsigned long total_render_power;
 
@@ -79,27 +86,33 @@ struct general_info
 

	
 
/* *********************************************
 
   Function Prototypes
 
   ********************************************* */
 

	
 
/* ************General Functions************* */
 
int distrend_do();
 
int distrend_do_config(int argc, char *argv[], struct distrend_config **config, multiio_context_t multiio);
 
int distrend_config_free(struct distrend_config *config);
 
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
 
 * client request handlers
 
 */
 
int distrend_handle_version(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 
int distrend_handle_file_post_start(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 
int distrend_handle_file_post(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 
int distrend_handle_file_post_finish(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data);
 

	
 
/* functions of some generic sort ...ish */
 
int distrend_handle_successful_upload(struct distrend_client *client, struct distrend_client_file_post *client_file_post);
 

	
 
/* **************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);
 

	
 
/* **************Test Functions**************** */
 
int interactiveTest(int test, multiio_context_t multiio, struct general_info *general_info);
 

	
 
/* **************** Main ********************* */
 
int main(int argc, char *argv[])
 
{
 
@@ -145,24 +158,28 @@ Ethan Zonca <ethanzonca@gmail.com>\n\
 
    }
 

	
 

	
 
  multiio = multiio_context_new();
 

	
 
  if(distrend_do_config(argc, argv, &general_info.config, multiio))
 
    return 1;
 

	
 
  /** preset paths */
 
  _distren_asprintf(&general_info.files.geninfo, "%s/general_info.xml",
 
		    general_info.config->datadir);
 

	
 
  _distren_asprintf(&general_info.files.tmpdir, "%s/tmp",
 
		    general_info.config->datadir);
 
  distren_mkdir_recurse(general_info.files.tmpdir);
 

	
 
  /** MySQL Connection */
 
  fprintf(stderr,"Connecting to mysql...\n");
 
  if(mysqlConnect(&general_info.conn,
 
		  general_info.config->mysql_user,
 
		  general_info.config->mysql_host,
 
		  general_info.config->mysql_pass,
 
		  general_info.config->mysql_database) )
 
    {
 
      fprintf(stderr, "%s:%d: mysqlConnect() failed\n", __FILE__, __LINE__);
 
      fprintf(stderr, "don't test mysql stuff\n");
 
      interactiveTest(test, multiio, &general_info);
 
      return 1;
 
@@ -181,24 +198,27 @@ Ethan Zonca <ethanzonca@gmail.com>\n\
 
  remoteio_generic_data_set(general_info.config->options->remoteio, general_info.config->listens);
 
  for(counter = 0; general_info.config->listen_ports[counter]; counter ++)
 
    {
 
      tmp = distrend_listen_add(general_info.config->listens, general_info.config->listen_ports[counter]);
 
      if(tmp)
 
	{
 
	  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_FILE_POST_START, &distrend_handle_file_post_start);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST, &distrend_handle_file_post);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_FILE_POST_FINISH, &distrend_handle_file_post_finish);
 

	
 
  /* Main Loop */
 
  general_info.config->die = 0;
 
  while(!general_info.config->die)
 
    {
 
      multiio_poll(multiio, 15000);
 

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

	
 
      /* Run the watchdog, @TODO: like every 10 mins or something */
 
      frame_watchdog(general_info.conn);
 
    }
 
@@ -256,24 +276,277 @@ int distrend_handle_version(struct gener
 
	  distrend_send_disconnect(client, tmp_str);
 
	  free(tmp_str);
 
	}
 
      else
 
	distrend_send_disconnect(client, "Invalid PACKAGE_VERSION :-|.");
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * Traversal helper for distrend_client_find_post().
 
 */
 
int distrend_client_find_post_traverse(uint32_t *post_id, struct distrend_client_file_post *client_file_post)
 
{
 
  if(*post_id == client_file_post->post_id)
 
    return FALSE;
 

	
 
  return TRUE;
 
}
 

	
 
/**
 
 * Find the record for an in-progress client's file posting.
 
 */
 
struct distrend_client_file_post *distrend_client_find_post(struct distrend_client *client, uint32_t post_id)
 
{
 
  if(list_traverse(client->file_post_list, &post_id, (list_traverse_func_t)&distrend_client_find_post_traverse, LIST_ALTR | LIST_FORW | LIST_FRNT) != LIST_EXTENT)
 
    return list_curr(client->file_post_list);
 
  return NULL;
 
}
 

	
 
/**
 
 * Finds a post_context based on the post_id and client.
 
 *
 
 * Compatible the distren_request_parse_file_post_find_context_func_t.
 
 */
 
static distren_request_file_post_context_t distrend_client_find_file_post_context(uint32_t post_id, void *client)
 
{
 
  struct distrend_client_file_post *client_file_post;
 

	
 
  client_file_post = distrend_client_find_post(client, post_id);
 
  if(client_file_post)
 
    return client_file_post->post_context;
 
  return NULL;
 
}
 

	
 
/**
 
 * Clean up and free a client_file_post
 
 *
 
 * Whenever calling this functino, you almost _always_ have to call
 
 * list_remove_element(client->file_post_list, client_file_post);
 
 * first.
 
 */
 
void distrend_client_file_post_free(struct distrend_client_file_post *client_file_post)
 
{
 
  fclose(client_file_post->fd);
 
  free(client_file_post->filename);
 
  unlink(client_file_post->file_save_path);
 
  free(client_file_post->file_save_path);
 

	
 
  free(client_file_post);
 
}
 

	
 
int distrend_handle_file_post_start(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data)
 
{
 
  struct distrend_client_file_post *client_file_post;
 

	
 
  distren_request_file_post_context_t post_context;
 
  uint32_t post_id;
 
  char *filename;
 

	
 
  char *str_tmp;
 

	
 
  int ret;
 

	
 
  /**
 
   * @todo access check!
 
   */
 
  fprintf(stderr, __FILE__ ":%d:distrend_handle_file_post_start(): You need to check if a client is actually allowed to upload files somehow!\n", __LINE__);
 

	
 
  /*
 
   * other servers should be excluded from this check, but we don't
 
   * store the servertype in client yet.
 
   */
 
  if(list_size(client->file_post_list) > 1)
 
    {
 
      distrend_send_disconnect(client, "You are trying to upload too many files at once!");
 
      return 1;
 
    }
 

	
 
  ret = distren_request_parse_file_post_start(req, req_data, &post_context, &post_id, &filename);
 
  if(ret)
 
    {
 
      distrend_send_disconnect(client, "You sent me an invalid DISTREN_REQUEST_FILE_POST_START packet");
 
      return 1;
 
    }
 

	
 
  if(distrend_client_find_post(client, post_id))
 
    {
 
      _distren_asprintf(&str_tmp, "Err accepting file: You are trying to upload using post_id=%d while you have already started another upload using the same post_id", post_id);
 
      distrend_send_disconnect(client, str_tmp);
 
      free(str_tmp);
 

	
 
      distren_request_file_post_context_free(post_context);
 

	
 
      return 1;
 
    }
 

	
 
  client_file_post = malloc(sizeof(struct distrend_client_file_post));
 
  if(!client_file_post)
 
    {
 
      distrend_send_disconnect(client, "Error accepting file: out of memory");
 

	
 
      distren_request_file_post_context_free(post_context);
 

	
 
      return 1;
 
    }
 

	
 
  client_file_post->post_context = post_context;
 
  client_file_post->post_id = post_id;
 
  client_file_post->filename = filename;
 
  _distren_asprintf(&client_file_post->file_save_path, "%s/conn-%d_file_post-%d",
 
		   geninfo->files.tmpdir, client->connection_id, post_id);
 
  client_file_post->fd = fopen(client_file_post->file_save_path, "w");
 
  if(!client_file_post->fd)
 
    {
 
      perror("fopen");
 

	
 
      fprintf(stderr, "error: Unable to open ``%s''. See above ``fopen'' error for more details.\n", client_file_post->file_save_path);
 
      distrend_send_disconnect(client, "Error accepting file: unable to store the file n disk");
 

	
 
      distren_request_file_post_context_free(post_context);
 

	
 
      list_remove_element(client->file_post_list, client_file_post);
 
      distrend_client_file_post_free(client_file_post);
 
      return 1;
 
    }
 

	
 
  list_insert_after(client->file_post_list, client_file_post, 0);
 

	
 
  return 0;
 
}
 

	
 
int distrend_handle_file_post(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data)
 
{
 
  struct distrend_client_file_post *client_file_post;
 

	
 
  void *file_data;
 
  size_t file_data_len;
 

	
 
  uint32_t post_id;
 
  char *tmp_str;
 

	
 
  size_t written_len;
 

	
 
  int ret;
 

	
 
  ret = distren_request_parse_file_post(req, req_data, &post_id,
 
					&distrend_client_find_file_post_context, client,
 
					&file_data, &file_data_len);
 
  if(ret)
 
    {
 
      distrend_send_disconnect(client, "You sent me an invalid DISTREN_REQUEST_FILE_POST packet");
 
      return 1;
 
    }
 

	
 
  client_file_post = distrend_client_find_post(client, post_id);
 

	
 
  if(!client_file_post)
 
    {
 
      _distren_asprintf(&tmp_str, "You are attempting to upload post_id=%d when you haven't given a DISTREN_REQUEST_FILE_POST_START packet", post_id);
 
      distrend_send_disconnect(client, tmp_str);
 
      free(tmp_str);
 
      return 1;
 
    }
 

	
 
  written_len = fwrite(file_data, 1, file_data_len, client_file_post->fd);
 
  if(written_len < file_data_len)
 
    {
 
      distrend_send_disconnect(client, "Error saving upload: error while writing to the temporary upload file");
 

	
 
      list_remove_element(client->file_post_list, client_file_post);;
 
      /* closes the file being written, and free()s everything, unlinks the file */
 
      distrend_client_file_post_free(client_file_post);
 

	
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * here be magic
 
 */
 
int distrend_handle_file_post_finish(struct general_info *geninfo, struct distrend_client *client, struct distren_request *req, void *req_data)
 
{
 
  struct distrend_client_file_post *client_file_post;
 

	
 
  uint32_t post_id;
 
  int ret;
 

	
 
  ret = distren_request_parse_file_post_finish(req, req_data, &post_id,
 
					       &distrend_client_find_file_post_context, client);
 
  if(ret)
 
    {
 
      switch(ret)
 
	{
 
	case 2:
 
	  /* client asked to cancel a file upload */
 
	  client_file_post = distrend_client_find_post(client, post_id);
 
	  if(client_file_post)
 
	    {
 
	      list_remove_element(client->file_post_list, client_file_post);
 
	      distrend_client_file_post_free(client_file_post);
 
	    }
 
	  return 0;
 

	
 
	case 3:
 
	  /* checksuming failed */
 
	  distrend_send_disconnect(client, "You have uploaded a file that doesn't match its checksum somehow... which should be pretty much impossible");
 
	  client_file_post = distrend_client_find_post(client, post_id);
 
	  if(client_file_post)
 
	    {
 
	      list_remove_element(client->file_post_list, client_file_post);
 
	      distrend_client_file_post_free(client_file_post);
 
	    }
 
	  return 1;
 

	
 
	default:
 
	  distrend_send_disconnect(client, "You sent me an invalid DISTREN_REQUEST_FILE_POST_FINISH packet");
 
	}
 
      return 1;
 
    }
 

	
 
  client_file_post = distrend_client_find_post(client, post_id);
 
  if(!client_file_post)
 
    return 1;
 

	
 
  /*
 
   * Here it is... manage a file being submitted for rendering... or
 
   * for whatever purpose it was uploaded for somehow...
 
   */
 
  distrend_handle_successful_upload(client, client_file_post);
 

	
 
  list_remove_element(client->file_post_list, client_file_post);
 
  distrend_client_file_post_free(client_file_post);
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * Does stuff with file uploads after they've been successfully acquired.
 
 *
 
 * @todo this should probably be genericized to handle 1. file uploads and 2. server-initiated file _downloads_, i.e., using libcurl. In that case, struct distrend_client_file_post shouldn't be passed directly to ourself but some common struct might be passed here.
 
 */
 
int distrend_handle_successful_upload(struct distrend_client *client, struct distrend_client_file_post *client_file_post)
 
{
 
  fprintf(stderr, __FILE__ ":%d: STUB: I don't know what to do with %s[%s] :-/\n", __LINE__,
 
	  client_file_post->filename, client_file_post->file_save_path);
 

	
 
  return 0;
 
}
 

	
 
/**
 
   Performs command stored in a client's request. @TODO: Fill stub
 
*/
 
int distrend_do()
 
{
 
  return 0;
 
}
 

	
 
/* Grabs config info from confs */
 
int distrend_do_config(int argc, char *argv[], struct distrend_config **config, multiio_context_t multiio)
 
{
 
  unsigned int counter;
 

	
 
@@ -573,12 +846,13 @@ int interactiveTest(int test, multiio_co
 
       break;
 
       
 
     case 0:
 
       test = 0;
 
       break;
 
     default:
 
       fprintf(stderr, "Invalid input, please try again.\n");
 
       break;
 
     }
 
   }
 
  return 0;
 
}
 

	
src/server/distrend.h
Show inline comments
 
@@ -34,13 +34,16 @@ struct distrend_config
 
  struct distrend_listens *listens;
 
  int *listen_ports;
 
  char *datadir;
 

	
 
  int die;
 

	
 
  char *mysql_user;
 
  char *mysql_host;
 
  char *mysql_pass;
 
  char *mysql_database;
 
};
 

	
 
struct distrend_client_file_post;
 
void distrend_client_file_post_free(struct distrend_client_file_post *client_file_post);
 

	
 
#endif
src/server/listen.c
Show inline comments
 
@@ -37,25 +37,26 @@
 
#include <unistd.h>
 

	
 
/* local */
 

	
 
struct distrend_request_handler_info
 
{
 
  enum distren_request_type request_type;
 
  distrend_handle_request_func_t handler;
 
};
 

	
 
struct distrend_client *distrend_client_new(struct distrend_listens *listens,
 
					    enum distrend_client_state state,
 
					    struct remoteio *rem);
 
					    struct remoteio *rem,
 
					    int connection_id);
 
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);
 
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);
 
int listen_handle_error(multiio_context_t multiio,
 
@@ -281,25 +282,25 @@ int listen_handle_accept(multiio_context
 
   struct distrend_client *newclient;
 

	
 
   int newclientsock;
 

	
 
   struct remoteio *rem;
 

	
 
   struct distren_request *req;
 
   void *data;
 

	
 
   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);
 
   newclient = distrend_client_new(listens, DISTREND_CLIENT_PREVERSION, NULL, 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, "accepted new connection; fd=%d\n", newclientsock);
 

	
 
   /* tabletennis */
 
   tabletennis_add_client(listens->tabletennis, newclient);
 
@@ -427,75 +428,79 @@ size_t distrend_listen_read_handle(struc
 

	
 
      distrend_dispatch_request(listens, rem, client, req, reqdata);
 
      free(reqdata);
 
      distren_request_free(req);
 

	
 
      /* I actually just used recursion in non-LISP code! :-D */
 
      return used_len + distrend_listen_read_handle(rem, listens, buf + used_len, len - used_len, client);
 
    }
 

	
 
  return used_len;
 
}
 

	
 

	
 
/**
 
 * 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);
 
  list_free(client->file_post_list, (list_dealloc_func_t)&distrend_client_file_post_free);
 

	
 
  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
 
*/
 
void remotio_send_to_client(struct distrend_client *client, const char *msg, size_t len)
 
{
 
    fprintf(stderr, "%s:%d: STUB I should queue data for writing to a client.... or should I? :-p\n", __FILE__, __LINE__);
 
}
 

	
 
/**
 
 * Allocates and initializes a struct distrend_client.
 
 *
 
 *
 
 */
 
struct distrend_client *distrend_client_new(struct distrend_listens *listens, enum distrend_client_state state, struct remoteio *rem)
 
struct distrend_client *distrend_client_new(struct distrend_listens *listens, enum distrend_client_state state, struct remoteio *rem, int connection_id)
 
{
 
  struct distrend_client *client;
 

	
 
  client = malloc(sizeof(struct distrend_client));
 
  if(!client)
 
    {
 
      fprintf(stderr, "OOM\n");
 

	
 
      return NULL;
 
    }
 
  client->state = state;
 
  client->cleanup_time = time(NULL) + DISTREND_LISTEN_AUTHTIME;
 
  client->inlen = 0;
 
  client->expectlen = 0;
 
  client->file_post_list = list_init();
 
  client->rem = rem;
 
  client->connection_id = connection_id;
 

	
 
  return client;
 
}
 

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

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

	
src/server/listen.h
Show inline comments
 
@@ -30,24 +30,25 @@ 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"
 
#include "common/request.h"
 

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

	
 
/**
 
   How long a client has after connecting to send
 
   authentication information before his connection is cleaned
 
   up.
 
 */
 
#define DISTREND_LISTEN_AUTHTIME 32
 

	
 
/**
 
@@ -95,47 +96,68 @@ struct distrend_listens
 
  /* 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;
 
  /*
 
   * the socket type reserved for us, i.e., the listen()/accept()
 
   * socket type whose events we handle.
 
  */
 
  multiio_socket_type_t socket_type;
 
};
 

	
 
struct distrend_client_file_post
 
{
 
  distren_request_file_post_context_t post_context;
 
  uint32_t post_id;
 
  /** user-friendly filename */
 
  char *filename;
 
  /** on-disk filename */
 
  char *file_save_path;
 
  FILE *fd;
 
};
 

	
 
/**
 
   The information necessary to recieve data from and send data
 
   to a client.
 
 */
 
struct distrend_client
 
{
 
  enum distrend_client_state state;
 

	
 
  /**
 
     The absolute time at which this client's entry in the client list will be
 
     expired, closed, and marked as dead so that it may be cleaned up. This is
 
     used to implement ping timeouts (if state == DISTREND_CLIENT_GOOD) and 
 
     disconnect message grace time (if state == DISTREND_CLIENT_BAD).
 
   */
 
  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;
 

	
 
  /**
 
   * File postings (uploads) are per-connection, and thus stored
 
   * here. Of type struct distrend_client_file_post.
 
   */
 
  list_t file_post_list;
 

	
 
  /**
 
   * Yes, in some places we need a unique key for this connection.
 
   */
 
  int connection_id;
 

	
 
  /**
 
   * 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;
 
};
 

	
 

	
 

	
 
/**
 
   A function signature that may be registered as a client
 
   request handler.
0 comments (0 inline, 0 general)