Changeset - abcf8952747b
AUTHORS
Show inline comments
 
DistRen is a joint initiative of [protofusion] (http://protofusion.org/) and Oh! No! Publishing (http://ohnopublishing.net/)
 

	
 
The principal devs are:
 
Ethan Zonca <ethanzonca@protofusion.org>
 
Ethan Zonca <ez@ethanzonca.com>
 
Nathan Phillip Brink <ohnobinki@ohnopublishing.net>
 
Matthew Orlando <onlylordofwar@gmail.com>
 
\ No newline at end of file
ChangeLog
Show inline comments
 
2009-01-04  Nathan Phillip Brink  <ohnobinki@gmail.com>
 

	
 
	* Initial construction of an autoconfy filestructure.
 

	
 
2010-05-18  Ethan Zonca <ez@ethanzonca.com>
 

	
 
	* Distrensimpleslave is now functional, uses php to work around the non-working server code
 
\ No newline at end of file
NEWS
Show inline comments
 
Nothing has happened yet. We have not had time to work on this yet, but have merely developed the concepts
 
\ No newline at end of file
 
2010-05-18  Ethan Zonca <ez@ethanzonca.com>
 

	
 
	* Basic rendering is now possible using http://dev.distren.org/ (see DistRenWeb repo) and distrensimpleslave,
 
		which uses a PHP interface to work around the nonfunctional server codebase.
 
\ No newline at end of file
README
Show inline comments
 
Welcome to the amazing DistRen C-based system. Completely nonfunctional and without a single line of 
 
code.
 
Welcome to the amazing DistRen C-based system. Think it's completely nonfunctional and without a single line of 
 
code? No longer.
 

	
 
Basic documentation resides on http://protofusion.org/wiki/DistRen_Manual, and relevant sections will be transferred to this file in the near future.
 
\ No newline at end of file
TODO
Show inline comments
 
Key:
 
*-An important Item that needs to be finished
 
B-Initial of person who needs to fix this (Binki)
 
M-LordofWar needs to fix this
 
E-normaldotcom needs to fix this
 
x-finished task
 
=-Long-term goal
 
?-needs reconsideration/explanation/rewording
 

	
 
Notes:
 
  Initial release will no longer feature SSH auth, distrend will handle auth telnet-style. User registration will be done via PHP.
 
  Simpleslave uses a PHP API to communicate with the mysql-backed server, allowing debugging while server software is nonfunctional.
 

	
 
Build
 

	
 
General
 
=-documenation: manpages
 
=-runtime support: should we eventually just go with APR, glib, maybe something from suckless people? Is libdaemon (or whatever's out there) useful?
 
=-XML (XML-RPC): useful as a communication protocol? as RPC protocol? see atheme; useful as job description format possibly.
 
*-remoteio: figure out how to handle communications; async + callbacks seems the best method to me atm
 

	
 
Master
 
*=Add lockout of slaves below a certain version
 
*-Complete testing with interactive test mode
 
 -update frame assigner to distribute some frames for lower-priority jobs when there's a higher priority job
 
B-Add calls to remotio -> make API for sending messages ~= a function for every type of message defined in protocol.h
 
 -Write a stub for getting info from the tarball/validifying the tarball. Read distren-job.xml, a file in the tarball, to find out 1. which rendering system to use (that system, e.g. blender/povray, can read more specifics, such as name of file to pass to blender and frames. Options common between different systems will be handled in common as best as possible)
 
 -Write a stub for publishing file and constructing job description so that the job can be shared
 
B-XML-RPC interface for obtaining info
 
=-charsets: the program should operate in UTF-8. This is partially required by libxml2's (and XML's) usage of UTF-8. We want the program's internal charset to be UTF-8 and figure out if we need w_char/whatever to fulfill UTF-8. I don't believe we do... mabye we just need a declaration that al data stored must be in UTF-8 format. -> we need to consider how charsets work when and if issues come up
 
M-explain members of struct general_info; e.g., what is timestamp used for, what's the point of the hibernate var, etc.
 
*-resolve conflict between liblist and libmysqlclient
 
*-figure out delete-during-traversal for liblist
 
 
 
Slave
 
 -Fix current bugs
 
*-Add calls to remoteio, once it works or even before it works -> write message API functions and API
 
=-Make code more flexible for different types of jobs and operating systems (such as paths, libs, etc)
 
=-Add code to allow pausing/resuming of the slave (including a running blender process) -> signal handling (SIGTSTP vs. SIGSTOP), distrenslave_control cmdline interface
 
B-Add tmpdir variable (just like the datadir variable) that is compile-time or whatever -> simple API to allow configuration-file override while taking into account defaults set by ./configure
 
*-Finish stub to delete old job data and output
 
=-Upload finished frames' output files while rendering subsequent frames
 
x-Add simpleslave with code to work around nonexisting server codebase
 

	
 
Options
 
=-Rewrite some stuff, try to make it simpler
 
x-Move server and client confuse code into their individual files, rather than in the common file. -- note: there wasn't any client code in options.c. There was the ``server'' section. That section is passed multiple times and provides information to remoteio on how to connect to servers
 
=-Review all confuse interfacing
 
B-Push patch for relative includes in confuse to the confuse developers -- windows/mingw32 testing required first
 
?-Expand execio() to support stderr
configure.ac
Show inline comments
 
# Copyright 2009 Nathan Phillip Brink, Ethan Zonca
 
# Copyright 2010 Nathan Phillip Brink, Ethan Zonca
 
#
 
# 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/>.
 

	
 
AC_PREREQ(2.61)
 
AC_INIT([distren],[0.0],[http://bugs.ohnopub.net/])
 
AC_CONFIG_SRCDIR([src/server/distrend.c])
 
AC_CONFIG_MACRO_DIR([m4])
 

	
 
AC_PROG_CC
 
AC_PROG_LIBTOOL
 
#AC_PROG_RANLIB #don't add this even if autoscan says to, because AC_PROG_LIBTOOL is enough
 

	
 
AM_INIT_AUTOMAKE([gnu dist-bzip2 subdir-objects -Wall])
 
AM_PROG_CC_C_O
 

	
 
#basic low-level checks (suggested by autoscan)
 
AC_CHECK_FUNCS([dup2])
 
AC_CHECK_FUNCS([memset])
 
AC_CHECK_FUNCS([strdup])
 

	
 
AC_CHECK_HEADERS([fcntl.h])
 
AC_CHECK_HEADERS([malloc.h])
 

	
 
AC_FUNC_FORK
 
AC_FUNC_MALLOC
 

	
 
AC_TYPE_PID_T
 
AC_TYPE_SIZE_T
 

	
 
# selective compilation
 
# For now, this is only left for when the C-based client is
 
# 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"])
 

	
 
# to grab GNU-specific function prototypes for the following functions:
 
# getline
 
AC_DEFINE([_GNU_SOURCE], [1])
 

	
 
#package dependencies:
 

	
 
PKG_PROG_PKG_CONFIG(0.17.2)
 

	
 

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

	
 
PKGCONFIG_OHNOWRAP([CHECK], [check >= 0.9.3])
 

	
 
# define paths for configuration files:
 

	
 
AC_DEFINE_DIR([LOCALSTATEDIR], [localstatedir], [Default directory for storing state information])
 
AC_DEFINE_DIR([RUNSTATEDIR], [localstatedir/run], [Default directory for registering runtime information like pid-files])
 

	
 
AC_CONFIG_FILES([Makefile
 
etc/distrendaemon.conf
 
etc/distrenslave.conf
 
])
 

	
 
AC_OUTPUT
src/client/distren.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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/>.
 
*/
 

	
 

	
 
/* This code is meant to submit files to the distren server(s). Let's say server for now. And let's state file purpose in each file just so we don't get screwed up! */
 

	
 

	
 
/* Ideas for php-side stuff:
 
 *
 
 * PHP adds the users... has captcha and email confirmation wth a pseudo-random number that
 
 * the user must enter on the site. Apache should be running mod_peruser for safety, and I'm
 
 * thinking zserver2 for web serving for convenience...
 
 */
 

	
 

	
 
#include "distren.h"
 

	
 
#include <stdio.h> /* sprintf, printf */
 
#include <stdlib.h> /* malloc, free */
 
#include <unistd.h> /* getopt */
 
#include <confuse.h>
 
#include <string.h> // for strcat
 

	
 

	
 
int main(int argc, char *argv[])
 
{
 
  int doLogin = 0;
 

	
 
  char *input;
 
  char *output;
 
  char *jobid;
 

	
 
  char *username;
 
  char *password;
 

	
 
  char curopt;
 

	
 
  distren_t distren;
 
  distren_job_t distren_job;
 

	
 
  input = NULL;
 
  output = NULL;
 

	
 
  while(-1 != (curopt = getopt(argc, argv, "i:o:u:p:h")))
 
    {
 
      if(curopt == ':')
 
	{
 
	  fprintf(stderr, "-%c: is missing an argument\n", optopt);
 
	  return 1;
 
	}
 
      else if(curopt == '?')
 
	{
 
	  fprintf(stderr, "-%c: invalid option specified\n", optopt);
 
	  return 1;
 
	}
 
      else if(curopt == 'h')
 
	{
 
	  fprintf(stderr, "Usage: %s -i <inputfile> -o <outputfile> [ -u <username> -p <password> ]\n", argv[0]);
 
	  fprintf(stderr, "\t-i\tSpecifies the input file\n\
 
\t-o\tSpecifies the output file\n\
 
\t-h\tShows this help message\n");
 
	  
 
	  //  don't return here because options_init will have an overall
 
	  //  options help page
 
	  
 
	}
 
      else if(curopt == 'u') {
 
        doLogin = 1;
 
        username = strdup(optarg);
 
      }
 
      else if(curopt == 'p') {
 
        doLogin = 1;
 
        password = strdup(optarg);
 
      }
 
      else if(curopt == 'i')
 
	
 
	 //  TODO: is strdup good or bad? what about
 
	 //  the idea that all libs should allow plugging different
 
	 //  malloc/free implementations in?
 
	 
 
	input = strdup(optarg);
 
      else if(curopt == 'o')
 
	output = strdup(optarg);
 
    }
 

	
 
  
 
     // give this error after the general arguments parsing so that
 
     // the general help from options_init can have effect
 
  if(!username || !password){
 
    fprintf(stderr, "Username and password must be specified");
 
    return 1;
 
  }
 
  if(doLogin == 1){
 
    fprintf(stderr, "Trying username and password on server...");
 
    return 0;
 
  }
 
  if(!input
 
     || !output)
 
    {
 
      fprintf(stderr, "Input and output files must be specified\n");
 
      return 1;
 
    }
 

	
 
  fprintf(stderr, "reading from %s\nwriting to %s\n", input, output);
 

	
 
  if(distren_init_mf(&distren, &malloc, &free))
 
    {
 
      fprintf(stderr, "error initializing distren handle\n");
 
      return 1;
 
    }
 

	
 
  if(distren_submit_file(distren, &distren_job, input))
 
    {
 
      fprintf(stderr, "error submitting file\n");
 
      return 1;
 
    }
 

	
 
  if(distren_job_getid(distren_job, &jobid))
 
    {
 
      fprintf(stderr, "error retrieving job id\n");
 
      return 1;
 
    }
 
  fprintf(stdout, "jobid: %s\n", jobid);
 

	
 
  if(distren_job_retrieve_file(distren_job, output))
 
    {
 
      fprintf(stderr, "error retrieving output file\n");
 
      return 1;
 
    }
 

	
 
  // if these following lines fail, it'll be reported as a core dump ;-)
 
  distren_job_free(distren_job);
 
  distren_free(distren);
 

	
 

	
 

	
 
 return 0;
 
};
 

	
 

	
 

	
 

	
 

	
src/client/distren.h
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 

	
 
  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_H
 
#define DISTREN_H 1
 

	
 
/*
 
  Public API for DistRen
 
 */
 

	
 
#include <stddef.h> /* size_t */
 

	
 
typedef struct distren *distren_t;
 

	
 
typedef void* (*distren_malloc_t)(size_t);
 
typedef void (*distren_free_t)(void*);
 

	
 
typedef struct distren_job *distren_job_t;
 

	
 
/**
 
 Initialize a libdistren handle. In the spirit of respecting client programs' choices,
 
 this initialization function allows malloc() and free() compatible function pointers
 
 to be passed.
 
*/
 
int distren_init_mf(distren_t *handle, distren_malloc_t mymalloc, distren_free_t myfree);
 

	
 
/**
 
 Initializes a libdistren handle using the system malloc() and free() implementation.
 
*/
 
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
 
*/
 
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_getid(distren_job_t job, char **jobid);
 

	
 
/**
 
   Retrieves a tarball of a rendering's result.
 
   @param outfile Filename to save the result to
 
 */
 
int distren_job_retrieve_file(distren_job_t job, const char *outfile);
 

	
 
/*
 
 TODO: extraction of a job-ID, retrieving job handle by job-ID, getting information
 
 for a job handle, retrieving results of a job, callbacks for progress (i.e., job_is_finished callback)
 
 -- threads?
 
*/
 

	
 
/**
 
 Destroys and frees a job handle
 
*/
 
int distren_job_free(distren_job_t job);
 

	
 
/**
 
 Destroys and frees a libdistren handle
 
*/
 
int distren_free(distren_t handle);
 

	
 
#endif
src/client/libdistren.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 

	
 
  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/>.
 
*/
 

	
 
/*
 
  Implementation of distren_* functions from distren.h excluding distren_job_* functions.
 
 */
 

	
 
#include "libdistren.h"
 

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

	
 
/**
 
   @todo needs to read configuration in
 
 */
 
int distren_init_mf(distren_t *handle, distren_malloc_t mymalloc, distren_free_t myfree)
 
{
 
  if(!handle
 
     || !mymalloc
 
     || !myfree)
 
    {
 
      fprintf(stderr, "distren_init_mf passed NULL pointers\n");
 
      return 1;
 
    }
 

	
 
  *handle = (*mymalloc)(sizeof(struct distren));
 
  if(!*handle)
 
    return 1;
 

	
 
  (*handle)->malloc = mymalloc;
 
  (*handle)->free = myfree;
 

	
 
  /* now the environment is ready for general use */
 
  if(_distren_getoptions(*handle))
 
    {
 
      fprintf(stderr, "error getting configuration\n");
 
      distren_free(*handle);
 
      return 1;
 
    }
 
  
 
  /**
 
     @todo open a connection to the server now and return error if we can't
 
     connect. 
 
   */
 
  
 
  return 0;
 
}
 

	
 
int distren_init(distren_t *handle)
 
{
 
  return distren_init_mf(handle, &malloc, &free);
 
}
 

	
 
/**
 
   @todo Stub
 
 */
 
int distren_submit_file(distren_t handle, distren_job_t *job, const char *filename)
 
{
 
  return 1;
 
}
 

	
 
/**
 

	
 
 */
 
int distren_free(distren_t handle)
 
{
 
  _free(handle, handle);
 
  return 0;
 
}
src/client/libdistren.h
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 

	
 
  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 LIBDISTREN_H_
 
#define LIBDISTREN_H_ 1
 

	
 
/*
 
  Private definitions for libdistren.
 
 */
 

	
 
#include "distren.h"
 

	
 
struct distren
 
{
 
  distren_malloc_t malloc;
 
  distren_free_t free;
 
  struct options_common *options; /*< use a pointer just to avoid #include "options.h"? */
 
  char *server;
 
};
 

	
 
struct distren_job
 
{
 
  char *jobid;
 
};
 

	
 
/*
 
  functions
 
*/
 

	
 
/**
 
   Avoid poluting the public namespace until we fix visibility.
 
 */
 
#define _malloc _distren_malloc
 
/**
 
   All of libdistren should use this rather than malloc.h's malloc.
 
 */
 
void *_malloc(distren_t distren, size_t size);
 

	
 
/**
 
   Avoid poluting the public namespace until we fix visibility.
 
 */
 
#define _free _distren_free
 
/**
 
   All of libdisren should use this instead of malloc.h's free()
 
*/
 
void _free(distren_t distren, void *tofree);
 

	
 
/**
 
   Sets up the distren handle with information garnered from
 
   configuration files, etc. Uses the environment variable
 
   DISTREN_CONFIG or the built-in default config file location.
 
 */
 
int _distren_getoptions(distren_t handle);
 

	
 
/**
 
   Unsets-up the distren handle with options loadable from a config file.
 
 */
 
int _distren_loseoptions(distren_t handle);
 

	
 
#endif
 

	
src/client/libdistren_config.c
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 

	
 
  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/>.
 
*/
 

	
 
/*
 
  Functions and code for retrieving the client's configuration information.
 
 */
 

	
 
#include "client/libdistren.h"
 
#include "common/options.h"
 

	
 
/**
 
@todo Stub
 
 */
 
int _distren_getoptions(distren_t handle)
 
{
 
  cfg_t *cfg;
 

	
 
  cfg_opt_t cfg_opts[] =
 
    {
 
      CFG_STR("server", NULL, 0),
 
      CFG_END()
 
    };
 
  
 
  if(options_init(0, NULL, &cfg, cfg_opts, "client", &handle->options))
 
    {
 
      fprintf(stderr, "error getting configuration\n");
 
      return 1;
 
    }
 
  
 
  handle->server = cfg_getstr(cfg, "server");
 
  fprintf(stderr, "using ``%s'' as server\n", handle->server);
 

	
 
  return 0;
 
}
 

	
 
/**
 
@todo stubish
 
 */
 
int _distren_loseoptions(distren_t handle)
 
{
 
  options_free(handle->options);
 
  
 
  return 0;
 
}
src/client/libdistren_job.c
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 

	
 
  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/>.
 
*/
 

	
 
/*
 
  Implementation of distren_job_* functions from distren.h.
 
 */
 

	
 
#include "libdistren.h"
 

	
 
/**
 
   @todo Stub
 
 */
 
int distren_job_getid(distren_job_t job, char **jobid)
 
{
 
  return 1;
 
}
 

	
 
/**
 
   @todo Stub
 
 */
 
int distren_job_retrieve_file(distren_job_t job, const char *outfile)
 
{
 
  return 1;
 
}
 

	
 
/**
 
   @todo Stub
 
 */
 
int distren_job_free(distren_job_t job)
 
{
 
  return 1;
 
}
src/client/libdistren_unbias.c
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matt Orlando
 

	
 
  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/>.
 
*/
 

	
 
/*
 
  Implementation of libdistren functions that exist to prevent this library from being biased toward certain methods of reporting errors/debug info or free()ing and malloc()ing
 
 */
 

	
 
#include "libdistren.h"
 

	
 
void *_malloc(distren_t distren, size_t size)
 
{
 
  return (*distren->malloc)(size);
 
}
 

	
 
void _free(distren_t distren, void *tofree)
 
{
 
  (*distren->free)(tofree);
 
}
src/common/execio.h
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca
 

	
 
  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_EXECIO_H
 
#define _DISTREN_EXECIO_H
 

	
 
/*
 
  This file tries to abstract away getting a socket/fd that talks to a spawned program
 
 */
 

	
 
#include <unistd.h>
 

	
 
enum execio_state
 
  {
 
    EXECIO_STATE_ERROR,
 
    EXECIO_STATE_EOF
 
  };
 

	
 

	
 
struct execio
 
{
 
  int pipe_write;
 
  int pipe_read;
 

	
 
  enum execio_state state;
 

	
 
  pid_t child;
 
};
 

	
 
/**
 
  runs progname with the arguments in argv. argv must be null terminated!!!!!!!!!
 

	
 
  returns nonzero return on error
 
*/
 
int execio_open(struct execio **eio, const char *progname, char *const argv[]);
 

	
 
/**
 
   doesn't block,
 
   returns 0 on success, 1 on failure
 
*/
 
int execio_read(struct execio *eio, void *buf, size_t len, size_t *bytesread);
 
int execio_write(struct execio *eio, void *buf, size_t len, size_t *byteswritten);
 

	
 
/**
 
  use this function to determine if the using program should keep trying to read/write
 

	
 
  @todo is this function good enough/necessary?
 
 */
 
enum execio_state execio_state(struct execio *eio);
 

	
 
/**
 
   Closes an execio session.
 
   @return nonzero on error 
 
*/
 
int execio_close(struct execio *eio);
 

	
 
#endif
 

	
src/common/libremoteio.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink
 
  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_LIBREMOTEIO_H
 
#define _DISTREN_LIBREMOTEIO_H
 

	
 
#include "remoteio.h"
 
#include <confuse.h>
 

	
 
/**
 
  private declarations for remoteio, to be included by options.c and remoteio.c
 
 */
 

	
 
enum remoteio_method
 
  {
 
    REMOTEIO_METHOD_SSH = 0,
 
#ifndef WINDOWS
 
    REMOTEIO_METHOD_UNIX = 1,
 
#endif
 
    REMOTEIO_METHOD_TCP = 2,
 
    /*    REMOETIO_METHOD_XMLRPC */ /*< maybe someday */
 
    REMOTEIO_METHOD_MAX = 3 /*< This is a number used to check the consitency of remoteio_server structs */
 
  };
 

	
 
struct remoteio_server
 
{
 
  struct remoteio_server *next;
 
  char *name; /*< The friendly named passed to remoteio_open() */
 
  char *hostname;
 
  char *username;
 
  enum remoteio_method method;
 
  unsigned int types; /*< See ``Server types'' in protocol.h */
 
};
 

	
 
struct remoteio_opts
 
{
 
  char *ssh_command;
 
  struct remoteio_server *servers;
 
};
 

	
 
struct remoteio {
 
  enum remoteio_method method;
 
  struct remoteio_opts *opts;
 
  struct execio *execio;
 
#ifndef WINDOWS
 
  int sock;
 
#endif
 
};
 

	
 

	
 
/* lookup table */
 
typedef int remoteio_open_func_t(struct remoteio *rem, struct remoteio_server *server);
 
typedef int remoteio_read_func_t(struct remoteio *rem, void *buf, size_t len, size_t *bytesread);
 
typedef int remoteio_write_func_t(struct remoteio *rem, void *buf, size_t len, size_t *bytesread);
 
typedef int remoteio_close_func_t(struct remoteio *rem);
 

	
 
struct remoteio_method_funcmap
 
{
 
  enum remoteio_method method;
 

	
 
  remoteio_open_func_t *open_func;
 
  remoteio_read_func_t *read_func;
 
  remoteio_write_func_t *write_func;
 
  remoteio_close_func_t *close_func;
 

	
 
  char *name; /*< used for error messages */
 
};
 

	
 
int remoteio_config(cfg_t *cfg, struct remoteio_opts *opts);
 

	
 
#endif
src/common/options.c
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca
 

	
 
  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 "options.h"
 

	
 
#include "common/asprintf.h"
 
#include "common/misc.h"
 
#include "common/libremoteio.h"
 
#include "common/protocol.h"
 

	
 
#include <confuse.h>
 
#include <string.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <unistd.h>
 

	
 
struct options_common_data
 
{
 
  cfg_t *cfg;
 
};
 

	
 

	
 
/**
 
   Not reentrant because of call to getenv()
 
   @todo replace abort()s with something useful
 
 */
 
int options_init(int argc, char *argv[], cfg_t **mycfg, cfg_opt_t *myopts, char *myname, struct options_common **allopts)
 
{
 
  char *configfileprefix;
 
  char *configfile;
 

	
 
  size_t i;
 

	
 
  char *optstring = "hc:";
 
  char curopt;
 

	
 
  char *workingdir;
 
  char *tmp;
 

	
 
  configfileprefix = NULL;
 
  while((curopt = getopt(argc, argv, optstring)) != (char)-1)
 
    switch(curopt)
 
      {
 
      case 'h':
 
	fprintf(stderr, "libdistren common options\n\
 
\n\
 
\t-h\tShow this help.\n\
 
\t-c <path>\tBasename for configuration files. For instance, if distrencommon.conf is located at /etc/distren/distrencommon.conf, set this option /etc/distren/distren . Default value: %s\n\
 
\n", SYSCONFDIR "/distren");
 
	return 2;
 
	break;
 
      case 'c':
 
	configfileprefix = strdup(optarg);
 
	if(!configfileprefix)
 
	  /* return is unnecessary here */
 
	  fprintf(stderr, "OOM\n");
 
	break;
 
      case ':':
 
	fprintf(stderr, "Option -%c requires an argument\n", optopt);
 
	return 1;
 
      }
 
  /* restore optind for other people who use getopt */
 
  optind = 1;
 
  
 
  if(!configfileprefix
 
     && (configfileprefix = getenv("DISTREN_CONFIG_PREFIX")) )
 
    {
 
      configfileprefix = strdup(configfileprefix);
 
      if(!configfileprefix)
 
	{
 
	  fprintf(stderr, "OOM\n");
 
	  return 1;
 
	}
 
    }
 
  /* For those of you who don't know, the following is an example of concatenation of constant strings by the C compiler. Now, that doesn't mean you can do run-time string concatenation ;-)
 
     strdup is because someday configfile will be customizable via argv[]
 
  */
 
  if(!configfileprefix)
 
      configfileprefix = strdup(SYSCONFDIR);
 
  if(!configfileprefix)
 
    {
 
      fprintf(stderr, "OOM\n");
 
      return 1;
 
    }
 

	
 
  workingdir = distren_getcwd();
 
  if(!workingdir)
 
    fprintf(stderr, "Error finding working directory, I will not be able to return to it after reading the configuration files\n");
 
  if(chdir(configfileprefix))
 
    fprintf(stderr, "Unable to chdir(\"%s\") where I expected to find config files, expect failure\n", configfileprefix);
 

	
 
  free(configfileprefix);
 

	
 
  _distren_asprintf(&configfile, "%s%s.conf", PACKAGE, myname);
 
  if(!configfile)
 
    {
 
      perror("blah");
 
      free(workingdir);
 
      return 1;
 
    }
 

	
 
  tmp = distren_getcwd();
 
  fprintf(stderr, "using configuration file: ``%s'' (CWD=``%s'')\n", configfile, tmp ? tmp : "");
 
  free(tmp);
 

	
 
  /* initialize structs */
 
  *allopts = malloc(sizeof(struct options_common));
 
  if(!*allopts)
 
    {
 
      perror("malloc");
 
      free(workingdir);
 
      free(configfile);
 
      return 1;
 
    }
 
  memset(*allopts, '\0', sizeof(struct options_common));
 

	
 
  (*allopts)->data = malloc(sizeof(struct options_common_data));
 
  if(!(*allopts)->data)
 
    {
 
      perror("malloc");
 
      free(workingdir);
 
      free(configfile);
 
      free(*allopts);
 
      return 1;
 
    }
 
  memset((*allopts)->data, '\0', sizeof(struct options_common_data));
 

	
 
  (*allopts)->remoteio = malloc(sizeof(struct remoteio_opts));
 
  if(!(*allopts)->data)
 
    {
 
      perror("malloc");
 
      free(workingdir);
 
      free(configfile);
 
      free((*allopts)->data);
 
      free(*allopts);
 
      return 1;
 
    }
 
  memset((*allopts)->remoteio, '\0', sizeof(struct remoteio_opts));
 
  (*allopts)->remoteio->ssh_command = strdup("ssh");
 
  /** @TODO: check for NULL return above */
 

	
 

	
 
  cfg_opt_t common_opts[] =
 
    {
 
      CFG_SIMPLE_STR("ssh-command", &(*allopts)->remoteio->ssh_command),
 
      CFG_END()
 
    };
 

	
 
  /**
 
    In these arrays, I should replace NULL with a pointer to a string in the struct which should hold the result of confuse processing an option. This is partially because confuse will not free the values it parses.
 
    the server sections in the distrencommon.conf describe different servers that the client may connect to
 
   */
 
  cfg_opt_t server_opts[] =
 
    {
 
      CFG_STR("username", NULL, CFGF_NONE),
 
      CFG_STR("hostname", NULL, CFGF_NONE),
 
      CFG_STR("method", "ssh", CFGF_NONE),
 
      CFG_STR_LIST("types", NULL, CFGF_NONE),
 
      CFG_END()
 
    };
 

	
 
  cfg_opt_t opts[] =
 
    {
 
      CFG_SEC("common",
 
	      common_opts,
 
	      CFGF_NONE),
 
      CFG_SEC("server",
 
	      server_opts,
 
	      CFGF_MULTI | CFGF_TITLE),
 
      CFG_SEC(myname,
 
	      myopts,
 
	      CFGF_NONE),
 
      CFG_FUNC("include",
 
	       &cfg_include),
 
      CFG_END()
 
    };
 

	
 

	
 

	
 
  (*allopts)->data->cfg = cfg_init(opts, 0);
 
  switch(cfg_parse((*allopts)->data->cfg, configfile))
 
    {
 
    default:
 
      fprintf(stderr, "cfg_parse returned an unknown error code\n");
 
    case CFG_FILE_ERROR:
 
      cfg_error((*allopts)->data->cfg, "Couldn't open file ``%s''\n", configfile);
 
      /* no break; on purpose */
 

	
 
    case CFG_PARSE_ERROR:
 

	
 
      fprintf(stderr, "error parsing configuration file, exiting\n");
 

	
 
      free((*allopts)->data);
 
      free(*allopts);
 
      free(configfile);
 

	
 
      return 1;
 

	
 
    case CFG_SUCCESS:
 
      break;
 
    }
 

	
 
  free(configfile);
 

	
 
  *mycfg = cfg_getsec((*allopts)->data->cfg, myname);
 

	
 
  if(chdir(workingdir))
 
    fprintf(stderr, "Unable to return to ``%s'', my old working directory. If you passed relative pathnames on the commandline, those pathnames will likely be disrespected\n", workingdir);
 
  free(workingdir);
 

	
 
  /*
 
   * libdistrencommon's config options:
 
   */
 

	
 
  /* remoteio -- iterate through servers */
 
  i = remoteio_config((*allopts)->data->cfg, (*allopts)->remoteio);
 
  if(i)
 
    {
 
      fprintf(stderr, "cleanup\n");
 
      abort();
 
      return 1;
 
    };
 

	
 
  return 0;
 
}
 

	
 
/**
 
   @todo free remoteio_servers
 
*/
 
int options_free(struct options_common *opts)
 
{
 
  /* free the other common_options struct's members */
 

	
 
  cfg_free(opts->data->cfg);
 

	
 
  free(opts->remoteio);
 
  free(opts->data);
 
  free(opts);
 

	
 
  return 0;
 
}
src/common/options.h
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca
 

	
 
  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/>.
 
*/
 

	
 
/*
 
  The point of this file is to handle options parsing that is common to both the server and client of distren. Options parsing that is specific to either should be managed by the server or the client. This means that the user of this interface will be given a handle to a part of a confuse config tree and have to eat that part of the tree to fulfill its own options.
 
  Unfortunately, this extra layer of abstraction shall possibly require a common_opts struct to be made that must be passed to all functions that use the common config. This would include any interface in /src/common/
 
 */
 

	
 
#ifndef _DISTREN_OPTIONS_H
 
#define _DISTREN_OPTIONS_H
 

	
 
#include <confuse.h>
 

	
 
/**
 
  incomplete:
 
  maybe remoteio.h should define its own struct which is injected into this struct and have its own handlers that parse its section of the confuse config file... (it'd be more modular)?
 
  Maybe we should just stick with compile-time modularity :-D (did I just suggest we make tons of macros?)
 
 */
 
struct options_common
 
{
 
  struct options_common_data *data;
 
  struct remoteio_opts *remoteio;
 
  struct client_opts *client;
 
  struct daemon_opts *daemon;
 
};
 

	
 
/**
 
  This function needs to eat some of your arguments to it, just like gtk+ and other libs like to. It particularly needs to be able to find the location of the config file, which should be taken from arguments if it's there. The argument list will have members that I recognize removed.
 

	
 
  Options should be kept alive during the program's life.
 

	
 
  The environment variable DISTREN_CONFIG_PREFIX, the built-in default config file location, or arguments passed on the commandline are used to determine the prefix of the config file name. Order of precedence: The command line is used first, then the environment variable, and then the default if no others sources specify the prefix.
 

	
 
  A calling function must call options_init() before processing its own arguments so that the libdistrencommon-specific help listing may be shown. The caller must also ignore libdistrencommon specific options. To avoid complication, those arguments are "c:h". Obviously, "h" should be handled sspecially ;-) (i.e., the caller should not ignore the "h" switch... it should output information about arguments it supports). Of course, calling functions could just rely on the 
 

	
 
  \param mycfg will be set to the section of the configuration file specific to the caller (client or server specific section). Who is supposed to free this variable?
 
  \param myopts holds the caller's confuse option declarations
 
  \param myname holds the name of the section of the config file that refers to mycfg and myopts -- currently ``slave'', ``client'', and ``server'' are valid
 

	
 
  \param allopts will be set to a pointer that many functions need for operation
 
  \param argc the argc that was passed to main() -- these will be used to determine the location of the config file
 
  \param argv the argv that was passed to main()
 
 */
 
int options_init(int argc, char *argv[], cfg_t **mycfg, cfg_opt_t *myopts, char *myname, struct options_common **allopts);
 

	
 
/**
 
   Frees the struct options_common. This should be run before the program exits.
 
 */
 
int options_free(struct options_common *allopts);
 
#endif
src/common/protocol.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink
 
  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 "protocol.h"
 

	
 
#include <malloc.h>
 

	
 
#define DISTREN_REQUEST_MAGIC (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));
 
  if(!newreq)
 
    {
 
      (*req) = NULL;
 
      return 1;
 
    }
 

	
 
  newreq->magic = DISTREN_REQUEST_MAGIC;
 
  newreq->len = len;
 
  newreq->enumsize = sizeof(enum distren_request_type);
 
  newreq->type = type;
 

	
 
  (*req) = newreq;
 
  return 0;
 
}
 

	
 
int distren_request_free(struct distren_request *req)
 
{
 
  free(req);
 
  return 0;
 
}
src/common/protocol.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink
 
  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_PROTOCOL_H
 
#define DISTREN_PROTOCOL_H
 

	
 
#include <stdint.h>
 

	
 
/**
 
   Server types:
 
 */
 
#define DISTREN_SERVERTYPE_SUBMIT (0x1)
 
#define DISTREN_SERVERTYPE_DISTRIBUTE (0x2)
 
#define DISTREN_SERVERTYPE_RENDER (0x4)
 

	
 
/**
 
   This file defines the constants and structs that the client uses to talk to the server and that the servers use to talk to eachother.
 
 */
 

	
 
/**
 
   generic, shared requests
 
 */
 
enum distren_request_type
 
  {
 
    DISTREN_REQUEST_VERSION = 1, /*< identifies the version of software being
 
				   used by the sender and tells if it is a client or server */
 
    DISTREN_REQUEST_PING = 2,
 
    DISTREN_REQUEST_PONG = 3,
 
    DISTREN_REQUEST_DISCONNECT = 4,
 

	
 
    /**
 
       client->server only requests
 
    */
 
    DISTREN_REQUEST_SUBMIT = 5,
 

	
 
    /**
 
       anything->server requests
 
     */
 
    DISTREN_REQUEST_JOBINFO = 6, /*< retrieves information about a job based on its number */
 

	
 
    /**
 
       server->anything
 
     */
 
    DISTREN_REQUEST_JOBINFO_RESPONSE = 7, /*< returns information about a job */
 

	
 
    /**
 
       server->server
 
    */
 
    DISTREN_REQUEST_RENDERFRAME = 8,
 
    DISTREN_REQUEST_DONEFRAME = 9,
 
    DISTREN_REQUEST_PROGRESS = 10, /*< tells another server of the progress of the first server's work at rendering */
 
    DISTREN_REQUEST_GETWORK = 11,
 

	
 
  };
 

	
 
struct distren_request
 
{
 
  uint32_t magic;
 
  uint32_t len;
 
  uint8_t enumsize;
 
  enum distren_request_type type;
 
};
 

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

	
 
/**
 
   frees request
 
 */
 
int distren_request_free(struct distren_request *req);
 

	
 
#endif
src/common/remoteio.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink
 
  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 "libremoteio.h"
 
#include "execio.h"
 
#include "asprintf.h"
 

	
 
#include <errno.h>
 
#ifndef _WIN32
 
#include <netdb.h>
 
#endif
 
#include <stdlib.h>
 
#include <stdio.h>
 
#include <string.h>
 
#include <sys/types.h>
 
#ifdef _WIN32
 
#include <winsock2.h>
 
#include <ws2tcpip.h>
 
#else
 
#include <sys/socket.h>
 
#endif
 
#include <unistd.h>
 

	
 
#ifndef _WIN32
 
#include <sys/un.h>
 
#endif
 

	
 
/* local */
 

	
 
#define REMOTEIO_DEFAULT_PORT "4050"
 

	
 
int _remoteio_ssh_open(struct remoteio *rem, struct remoteio_server *server);
 
int _remoteio_ssh_read(struct remoteio *rem, void *buf, size_t len, size_t *bytesread);
 
int _remoteio_ssh_write(struct remoteio *rem, void *buf, size_t len, size_t *byteswritten);
 
int _remoteio_ssh_close(struct remoteio *rem);
 

	
 
#ifndef _WIN32
 
int _remoteio_sock_open(struct remoteio *rem, struct remoteio_server *server);
 
int _remoteio_sock_close(struct remoteio *rem);
 
#endif
 
int _remoteio_sock_read(struct remoteio *rem, void *buf, size_t len, size_t *bytesread);
 
int _remoteio_sock_write(struct remoteio *rem, void *buf, size_t len, size_t *byteswritten);
 

	
 
int _remoteio_tcp_open(struct remoteio *rem, struct remoteio_server *server);
 
int _remoteio_tcp_close(struct remoteio *rem);
 

	
 
/**
 
  lookup table for different methods of remoteio:
 
  the enum remoteio_method is the index of the entry to use for that method. 
 
  Regardless, a NULL terminator is required because the configuration function
 
  searches through this table for the method specified in the config file.
 
*/
 
struct remoteio_method_funcmap funcmap[] = 
 
  {
 
    /* [REMOTEIO_METHOD_SSH] */
 
    {REMOTEIO_METHOD_SSH, &_remoteio_ssh_open, &_remoteio_ssh_read, &_remoteio_ssh_write, &_remoteio_ssh_close, "ssh"},
 
#ifndef _WIN32
 
    {REMOTEIO_METHOD_UNIX, &_remoteio_sock_open, &_remoteio_sock_read, &_remoteio_sock_write, &_remoteio_sock_close, "unix"},
 
#endif
 
    {REMOTEIO_METHOD_TCP, &_remoteio_tcp_open, &_remoteio_sock_read, &_remoteio_sock_write, &_remoteio_tcp_close, "tcp"},
 
    {REMOTEIO_METHOD_MAX, NULL, NULL, NULL, NULL, NULL}
 
  };
 

	
 
struct remoteio_server *remoteio_getserver(const struct remoteio_opts *opts, const char *servername);
 

	
 
int remoteio_config(cfg_t *cfg, struct remoteio_opts *opts)
 
{
 
  size_t numservers;
 
  size_t counter;
 
  static int haslisted_methods = 0;
 
  
 
  struct remoteio_server *aserver;
 

	
 
  opts->servers = malloc(sizeof(struct remoteio_server));
 
  if(!opts->servers)
 
    {
 
      fprintf(stderr, "@todo cleanup!\n");
 
      abort();
 
    }
 
  
 
  aserver = opts->servers;
 
  
 
  numservers = cfg_size(cfg, "server"); 
 
  for(counter = 0; counter < numservers; counter ++)
 
    {
 
      cfg_t *cfg_aserver;
 
      char *method;
 
      
 
      cfg_aserver = cfg_getnsec(cfg, "server", counter);
 
      
 
      if(!aserver) /*< if the malloc in the previous loop failed */
 
	abort();
 
      
 
      aserver->name = strdup(cfg_title(cfg_aserver));
 
      aserver->hostname = strdup(cfg_getstr(cfg_aserver, "hostname"));
 
      aserver->username = strdup(cfg_getstr(cfg_aserver, "username"));
 

	
 
      aserver->method = REMOTEIO_METHOD_MAX;
 
      method = cfg_getstr(cfg_aserver, "method");
 
      for(counter = 0; funcmap[counter].name; counter ++)
 
	if(strcmp(method, funcmap[counter].name) == 0)
 
	  aserver->method = REMOTEIO_METHOD_SSH;
 
      if(aserver->method == REMOTEIO_METHOD_MAX)
 
	{
 
	  fprintf(stderr, "No such method as %s\n", method);
 
	  if(!haslisted_methods)
 
	    {
 
	      fprintf(stderr, "Available methods:\n");
 
	      for(counter = 0; funcmap[counter].name; counter ++)
 
		fprintf(stderr, "\t%s\n", funcmap[counter].name);
 
	      
 
	      haslisted_methods ++;
 
	    }
 
	  abort();
 
	}
 
      
 
      if(counter < numservers - 1)
 
	{
 
	  aserver->next = malloc(sizeof(struct remoteio_server));
 
	  aserver = aserver->next;
 
	}
 
    }
 
  aserver->next = NULL;
 
  
 
  return 0;
 
}
 

	
 

	
 

	
 
int remoteio_open(struct remoteio **remoteio, struct remoteio_opts *opts, const char *servername)
 
{
 
  struct remoteio_server *theserver;
 
  struct remoteio *rem;
 

	
 
  int tmp;
 

	
 
  if(!opts)
 
    {
 
      fprintf(stderr, "%s:%d: no null opts!\n\tThis is a bug, please report it (after making sure it isn't already reported)\n", __FILE__, __LINE__);
 
      return 1;
 
    }
 

	
 
  theserver = remoteio_getserver(opts, servername);
 
  if(!theserver)
 
    {
 
      fprintf(stderr, "%s:%d: Could not find server named ``%s''\n", __FILE__, __LINE__, servername);
 
      return 1;
 
    }
 

	
 
  if(theserver->method >= REMOTEIO_METHOD_MAX
 
     || 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;
 
    }
 
  rem = malloc(sizeof(struct remoteio));
 
  if(!rem)
 
    {
 
      fprintf(stderr, "OOM\n");
 
      return 2;
 
    }
 
  *remoteio = rem;
 

	
 
  rem->method = theserver->method;
 
  rem->opts = opts;
 

	
 
  tmp = funcmap[theserver->method].open_func(rem, theserver);
 
  if(tmp)
 
    {
 
      fprintf(stderr, "Error using method %s for server ``%s''", funcmap[theserver->method].name, servername);
 
      free(rem);
 
      *remoteio = NULL;
 
      return tmp;
 
    }
 
  
 
  return 0;
 
}
 

	
 
int remoteio_read(struct remoteio *rem, void *buf, size_t len, size_t *bytesread)
 
{
 
  return funcmap[rem->method].read_func(rem, buf, len, bytesread);
 
}
 

	
 
int remoteio_write(struct remoteio *rem, void *buf, size_t len, size_t *byteswritten)
 
{
 
  return funcmap[rem->method].write_func(rem, buf, len, byteswritten);
 
}
 

	
 
int remoteio_close(struct remoteio *rem)
 
{
 
  int rtn;
 
  
 
  rtn = funcmap[rem->method].close_func(rem);
 
  free(rem);
 
  
 
  return rtn;
 
}
 

	
 

	
 
struct remoteio_server *remoteio_getserver(const struct remoteio_opts *opts, const char *servername)
 
{
 
  struct remoteio_server *aserver;
 
  
 
  for(aserver = opts->servers;
 
      aserver;
 
      aserver = aserver->next)
 
    if(!strcmp(servername, aserver->name))
 
      return aserver;
 

	
 
  return (struct remoteio_server *)NULL;
 
}
 

	
 

	
 

	
 

	
 
/**
 
   different remoteio methods' implementations:
 
 */
 

	
 
/*
 
  SSH, via execio
 
*/
 

	
 
int _remoteio_ssh_open(struct remoteio *rem, struct remoteio_server *server)
 
{
 
  char *userhost;
 
  char *sshargs[] = {rem->opts->ssh_command, NULL /* userhost */, "distrend", "-d", (char *)NULL};
 

	
 
  int rtn;
 

	
 
  if(server->username)
 
    _distren_asprintf(&userhost, "%s@%s", server->username, server->hostname);
 
  else
 
    userhost = strdup(server->hostname);
 
  sshargs[1] = userhost;
 

	
 
  rtn = execio_open( &rem->execio, "ssh", sshargs);
 
  if(rtn)
 
    {
 
      fprintf(stderr, "error opening remoteio channel to ssh userhost ``%s''\n" , userhost);
 
      free(userhost);
 
      return 1;
 
    }
 
  free(userhost);
 
  
 
  return 0;
 
}
 

	
 
int _remoteio_ssh_read(struct remoteio *rem, void *buf, size_t len, size_t *bytesread)
 
{
 
  return execio_read(rem->execio, buf, len, bytesread);
 
}
 

	
 
int _remoteio_ssh_write(struct remoteio *rem, void *buf, size_t len, size_t *byteswritten)
 
{
 
  return execio_write(rem->execio, buf, len, byteswritten);
 
}
 

	
 
int _remoteio_ssh_close(struct remoteio *rem)
 
{
 
  int rtn;
 
  
 
  rtn = execio_close(rem->execio);
 
  if(rtn)
 
    fprintf(stderr, "%s:%d: error closing execio\n", __FILE__, __LINE__);
 
  
 
  return rtn;
 
}
 

	
 
#ifndef _WIN32
 
/*
 
  local sockets implementation (``named pipes''), unix-only
 
 */
 
int _remoteio_sock_open(struct remoteio *rem, struct remoteio_server *server)
 
{
 
  int sock;
 
  struct sockaddr_un sockaddr;
 

	
 
  /*
 
    The POSIX docs pretty much say that I can't depend on sockpath being able to be longer than 
 
    some proprietary length. So, if the compiler specifies a long path for RUNSTATEDIR, it could
 
    cause a buffer overflow.
 
   */
 
  char *sockpath = RUNSTATEDIR "/distrend.sock";
 
  unsigned int sockaddr_len;
 

	
 
  sock = socket(AF_UNIX, SOCK_STREAM, 0);
 
  if(sock == -1)
 
    {
 
      perror("socket");
 
      return 1;
 
    }
 

	
 
  sockaddr.sun_family = AF_UNIX;
 
  /*
 
    The terminating NULL should not be included in what's copied to sun_path,
 
    although it won't hurt as long as strlen(sockpath) < max socket length
 
   */
 
  for(sockaddr_len = 0; sockpath[sockaddr_len]; sockaddr_len ++)
 
    sockaddr.sun_path[sockaddr_len] = sockpath[sockaddr_len];
 

	
 
  if(connect(sock, (struct sockaddr *)&sockaddr, sockaddr_len) == -1)
 
    {
 
      perror("connect");
 
      close(sock);
 
      return 1;
 
    }
 

	
 
  rem->sock = sock;
 

	
 
  return 0;
 
}
 

	
 
int _remoteio_sock_close(struct remoteio *rem)
 
{
 
  close(rem->sock);
 

	
 
  return 0;
 
}
 

	
 
#endif
 

	
 
int _remoteio_sock_read(struct remoteio *rem, void *buf, size_t len, size_t *bytesread)
 
{
 
  ssize_t readrtn;
 

	
 
  readrtn = read(rem->sock, buf, len);
 
  /*
 
    The following is valid for blocking sockets:
 
   */
 
  if(readrtn == -1)
 
    {
 
      /*
 
	in this case, we may have been interrupted by a signal and errno == EINTR
 
	or the connection was reset and errno = ECONNRESET
 

	
 
	Some of these are not error conditions:
 
       */
 
      perror("read");
 
      *bytesread = 0;
 

	
 
      if(errno != EINTR)
 
	{
 
	  fprintf(stderr, "error reading socket in remoteio_sock_read\n");
 
	  return 1;
 
	}
 

	
 
      return 0;
 
    }
 

	
 
  *bytesread = readrtn;
 
  if(!readrtn)
 
    {
 
      /*
 
	means EOF except when FD is in ``message-nondiscard'' or ``message-discard''
 
	modes.
 
       */
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 
int _remoteio_sock_write(struct remoteio *rem, void *buf, size_t len, size_t *byteswritten)
 
{
 
  ssize_t writertn;
 

	
 
  writertn = write(rem->sock, buf, len);
 

	
 
  if(writertn == -1)
 
    {
 
      perror("write");
 
      if(errno != EINTR)
 
	{
 
	  fprintf(stderr, "error writing to socket in remoteio_sock_write()\n");
 
	  return 1;
 
	}
 
    }
 

	
 
  *byteswritten = writertn;
 
  if(!writertn)
 
    {
 
      /*
 
	should we consider this an error? I'm pretty
 
	sure we should :-/
 
       */
 
      fprintf(stderr, "write() returned 0 in remoteio_sock_write()\n");
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 
/**
 
   TCP, taking advantage of the two generic socks read/write functions:
 
 */
 
int _remoteio_tcp_open(struct remoteio *rem, struct remoteio_server *server)
 
{
 
  int tmp;
 
  int tmp2;
 

	
 
  int sock;
 

	
 
  char *hostname;
 
  char *port;
 

	
 
  struct addrinfo addrinfo_hints;
 
  struct addrinfo *addrinfo_res;
 

	
 
  /**
 
     only hostname should be free()-ed, not port,
 
     because both are from the same block of malloc()-ed
 
     memory
 
   */
 
  hostname = strdup(server->hostname);
 
  for(port = hostname;
 
      *port && *port != ':';
 
      port ++)
 
    ;
 
  if(*port)
 
    {
 
      *port = '\0';
 
      port ++;
 
    }
 
  else
 
    port = REMOTEIO_DEFAULT_PORT;
 

	
 
  memset(&addrinfo_hints, '\0', sizeof(struct addrinfo));
 
  addrinfo_hints.ai_family = AF_UNSPEC;
 
#ifdef _WIN32
 
  /* windows lacks stuff documented in POSIX, I guess :-( */
 
  addrinfo_hints.ai_flags = 0;
 
#else
 
  addrinfo_hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
 
#endif
 
  addrinfo_hints.ai_socktype = SOCK_STREAM;
 

	
 
  tmp = getaddrinfo(server->hostname, port, &addrinfo_hints, &addrinfo_res);
 
  if(tmp)
 
    fprintf(stderr, "error resolving %s:%s: %s\n", server->hostname, port, gai_strerror(tmp));
 
  fprintf(stderr, "connecting to %s[%s]:%s\n", server->hostname, addrinfo_res->ai_canonname, port);
 

	
 
  free(hostname);
 

	
 
  sock = socket(addrinfo_res->ai_family, SOCK_STREAM, addrinfo_res->ai_protocol);
 
  if(sock == -1)
 
    {
 
      perror("socket");
 
      freeaddrinfo(addrinfo_res);
 
  
 
      return 1;
 
    }
 

	
 
  tmp = connect(sock, addrinfo_res->ai_addr, addrinfo_res->ai_addrlen);
 
  tmp2 = errno;
 
  freeaddrinfo(addrinfo_res);
 
  errno = tmp2;
 
  if(tmp == -1)
 
    {
 
      perror("connect");
 
      close(sock);
 

	
 
      return 1;
 
    }
 

	
 
  rem->sock = sock;
 

	
 
  return 0;
 
}
 

	
 
int _remoteio_tcp_close(struct remoteio *rem)
 
{
 
  close(rem->sock);
 

	
 
  return 0;
 
}
src/common/remoteio.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca
 

	
 
  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_REMOTEIO_H
 
#define _DISTREN_REMOTEIO_H
 

	
 
#include <stdlib.h>
 

	
 
/*
 
  RemoteIO provides an abstraction to the method of talking to a remote distrend. It is a layer on top of execio that should provide an equivalent interface.
 
 */
 

	
 
struct remoteio_opts;
 
struct remoteio;
 

	
 
/**
 
   Opens connection with remote distrend. Returns 1 on error.
 

	
 
   @todo should this be asynchronous?
 
 */
 
int remoteio_open(struct remoteio **rem, struct remoteio_opts *opts, const char *servername);
 

	
 
/**
 
   non-blocking I/O.
 
   @param len must be greater than 0
 

	
 
   @return 0 on success, 1 on failure. 
 
 */
 
int remoteio_read(struct remoteio *rem, void *buf, size_t len, size_t *bytesread);
 
int remoteio_write(struct remoteio *rem, void *buf, size_t len, size_t *byteswritten);
 

	
 
/**
 
   Closes a remoteio session.
 
   @return nonzero on error
 
*/
 
int remoteio_close(struct remoteio *rem);
 

	
 
#endif
src/server/distrend.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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/>.
 

	
 
*/
 

	
 
/* This file contains the code which both processes (renders) jobs as a slave, and the code which distributes frames to slaves after receiving them from the client portion of the codebase. */
 

	
 
#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 <confuse.h>
 
#include <malloc.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>
 
#include <libxml/xmlmemory.h>
 
#include <libxml/xmlreader.h>
 
#include <libxml/xmlwriter.h>
 

	
 
/* ******************* Structs ************************ */
 
struct general_info
 
{
 
  struct distrenjob head;
 
  distrend_mysql_conn_t conn;
 

	
 
  struct distrend_config *config;
 

	
 
  struct
 
  {
 
    /** general_info.xml */
 
    char *geninfo;
 
    
 
  } 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;
 
  unsigned long total_priority_pieces;
 
};
 

	
 

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

	
 
/* ************General Functions************* */
 
int distrend_do();
 
void frame_watchdog(struct distrenjob *distrenjob_head);
 
int distrend_do_config(int argc, char *argv[], struct distrend_config **config);
 
int distrend_config_free(struct distrend_config *config);
 

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

	
 
/* **************** Main ********************* */
 
int main(int argc, char *argv[])
 
{
 
  /* Parse arguments */
 
  int counter;
 
  int test = 0; /*< Interactive mode if 1 */
 
  struct general_info general_info;
 
  struct distrend_clientset *clients;
 

	
 
  enum clientstatus
 
  {
 
    CLIENTSTATUS_UNINITIALIZED = 0,
 
    CLIENTSTATUS_BUSY = 1,
 
    CLIENTSTATUS_IDLE = 2
 
  } clientstatus;
 

	
 
  clientstatus = CLIENTSTATUS_UNINITIALIZED;
 
  // xmlinit();
 

	
 
  for(counter = 0; counter < argc; counter ++)
 
    {
 
      if(strcmp(argv[counter], "-h") == 0)
 
      {
 
    	  fprintf(stderr, "Usage: distrend [option] \nStarts the distrend server\n\t-h\tshow this help\n\t-t\tlaunches queue testing interface \n");
 
	  return 2;
 
      }
 

	
 
      else if(strcmp(argv[counter], "-t") == 0)
 
      {
 
    	  fprintf(stderr, "Entering into test mode...\n\n");
 
    	  test = 1;
 
      }
 
    }
 

	
 

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

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

	
 
  /** MySQL Connection */
 
  fprintf(stderr,"Connecting to mysql...\n");
 
  if(mysqlConnect(&general_info.conn))
 
    {
 
      fprintf(stderr, "%s:%d: mysqlConnect() failed\n", __FILE__, __LINE__);
 
      fprintf(stderr, "don't test mysql stuff\n");
 
      interactiveTest(test, &general_info);
 
      return 1;
 
    }
 
  fprintf(stderr,"Finished connecting!\n");
 

	
 
  /** Execute test function */
 
  interactiveTest(test, &general_info);
 

	
 
  distrend_listen(general_info.config, &clients);
 

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

	
 
      distrend_accept(general_info.config, clients);
 

	
 
      /* Make the following code more event-driven */
 
      frame_watchdog(&general_info.head);
 

	
 
      struct frameset frame;
 
      struct distrenjob *job;
 
      distrenjob_new(&job);
 

	
 
      memset(&frame, '\0', sizeof(frame));
 

	
 
      /* If the client is idle, must be modified for climbing through linked list of clients (client->clientnum) */
 
      if(clientstatus == CLIENTSTATUS_IDLE)
 
	{
 
	  int returnnum = find_jobframe(general_info.conn, slaveKey, &job->jobnum, &frame.num); // Finds a frame to render @FIXME: Slavenum :D
 
	  if(returnnum)
 
	    {
 
	      fprintf(stderr,"No frames are available to render at this time. Idling...\n");
 
	      sleep(10);
 
	    }
 
	  else
 
	    remotio_send_to_client(frame.num, job->jobnum); // Pseudo-sends data to client
 
	}
 
      /* If the client states that they finished the frame */
 
      if(clientsays == DISTREN_REQUEST_DONEFRAME)
 
	{
 
	  clientstatus = CLIENTSTATUS_IDLE; // Sets the client back to idle
 
	  finish_frame(general_info.conn, 0, job->jobnum, frame.num); // @TODO: Make sure this actually works.
 
	}
 
      distrenjob_free(&job);
 
    }
 

	
 
  distrend_unlisten(general_info.config->listens, clients);
 
  distrend_config_free(general_info.config);
 

	
 
  xmlcleanup();
 

	
 
  /** free() paths */
 
  free(general_info.files.geninfo);
 
  mysqlDisconnect(general_info.conn);
 

	
 
  return 0;
 
}
 

	
 
/* ********************** Functions ************************* */
 

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

	
 

	
 
/** Checks for dead, latent, or stale slaves */
 
void frame_watchdog(struct distrenjob *distrenjob_head)
 
{
 
  // Replace with mysqlness, maybe
 
  // iterate through jobs
 
    // if the job has been started, checks by seeing if either to first or second frame has been started
 
    // FRAMESETSTATUS_UNASSIGNED
 
      // iterate through all frames for this job:
 
          //watchdog_forgiveness = seconds of forgiveness before frame is re-assigned:
 
             //   If frame is not completed within the number of seconds specified by watchdog_forgiveness
 
             //   Then change the frame status to unassigned
 
}
 

	
 

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

	
 
  cfg_opt_t myopts_listen[] =
 
    {
 
      CFG_SIMPLE_STR("type", NULL),
 
      CFG_SIMPLE_STR("path", NULL),
 
      CFG_SIMPLE_INT("port", NULL),
 
      CFG_END()
 
    };
 
  cfg_opt_t myopts[] =
 
    {
 
      CFG_SEC("listen",  /* this must be imported into struct listens (which must still be declared) */
 
          myopts_listen,
 
          CFGF_MULTI),
 
      CFG_SIMPLE_STR("datadir", NULL),
 
      CFG_STR_LIST("render_types", NULL, CFGF_NONE),
 
      CFG_END()
 
    };
 

	
 
  cfg_t *cfg_listen;
 

	
 
  fprintf(stderr, "%s:%d: running config\n", __FILE__, __LINE__);
 
  *config = malloc(sizeof(struct distrend_config));
 
  myopts[1].simple_value = &(*config)->datadir;
 

	
 
  if(options_init(argc, argv, &(*config)->mycfg, myopts, "daemon", &(*config)->options))
 
    return 1;
 

	
 
  /**
 
     grab listen blocks:
 
   */
 
  (*config)->listens = malloc(sizeof(struct distrend_listen) * (cfg_size((*config)->mycfg, "listen") + 1));
 
  for(counter = 0; counter < cfg_size((*config)->mycfg, "listen"); counter ++)
 
    {
 
      cfg_listen = cfg_getnsec((*config)->mycfg, "listen", counter);
 
      (*config)->listens[counter].port = cfg_getint(cfg_listen, "port");
 
      (*config)->listens[counter].sock = -1;
 
    }
 
  memset(&(*config)->listens[counter], '\0', sizeof(struct distrend_listen));
 

	
 
  fprintf(stderr, "using %s as datadir\n", (*config)->datadir);
 

	
 
  return 0;
 
}
 
int distrend_config_free(struct distrend_config *config)
 
{
 
  options_free(config->options);
 
  free(config);
 

	
 
  return 0;
 
}
 
/* ************************** XML Functions ************************* */
 

	
 
// writes the general_info.xml file which is a copy of the general_info structure
 
// except that it doesn't hold free_clients and rendering_clients
 
void update_general_info(struct general_info *geninfo)
 
{
 
  xmlTextWriterPtr writer;
 
  char *tmp;
 

	
 
  writer = xmlNewTextWriterFilename(geninfo->files.geninfo, 0);
 
  xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL);
 

	
 
  xmlTextWriterStartElement(writer, (xmlChar*)"general_info");
 

	
 
  _distren_asprintf(&tmp, "%d", geninfo->jobs_in_queue);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"jobs_in_queue", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  _distren_asprintf(&tmp, "%d", geninfo->total_finished_jobs);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"total_finished_jobs", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  _distren_asprintf(&tmp, "%d", geninfo->total_frames_rendered);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"total_frames_rendered", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  _distren_asprintf(&tmp, "%d", geninfo->highest_jobnum);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"highest_jobnum", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  xmlTextWriterEndDocument(writer);
 
  xmlFreeTextWriter(writer);
 
}
 

	
 
/**
 
   Reads general state information from general_info.xml
 
   into the general_info structure.
 
*/
 
int import_general_info(struct general_info *general_info)
 
{
 
  xmlDocPtr doc;
 
  xmlNodePtr cur;
 

	
 
  doc = xmlParseFile(general_info->files.geninfo);
 
  cur = xmlDocGetRootElement(doc);
 
  if (xmlStrcmp(cur->name, (xmlChar*)"general_info"))
 
    {
 
      fprintf(stderr, "xml document is wrong type");
 
      xmlFreeDoc(doc);
 
      return 1;
 
    }
 

	
 
  cur = cur->xmlChildrenNode;
 
  general_info->jobs_in_queue = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 
  cur = cur->next;
 
  general_info->total_finished_jobs = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 
  cur = cur->next;
 
  general_info->total_frames_rendered = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 
  cur = cur->next;
 
  general_info->highest_jobnum = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 

	
 
  general_info->hibernate = 0;
 
  general_info->free_clients = 0;
 
  general_info->rendering_clients = 0;
 
  general_info->timestamp = 0;
 
  general_info->total_render_power = 0;
 
  general_info->total_priority_pieces = 0;
 

	
 
  xmlFreeDoc(doc);
 

	
 
  return 0;
 
}
 

	
 

	
 
/**
 
   updates job_list.xml which lists all the jobs in the queue
 
   @return 0 means success
 
*/
 
// @QUERY: Likely obselete (don't remove at request of ohnobinki)
 
int update_xml_joblist(struct general_info *geninfo)
 
{
 
  struct distrenjob *job;
 
  xmlTextWriterPtr writer;
 
  char *tmp;
 
  int counter;
 

	
 
  /**
 
     update timestamp
 
  */
 
  geninfo->timestamp ++;
 
  if(geninfo->timestamp > 65530)
 
    geninfo->timestamp = 0;
 

	
 
  _distren_asprintf(&tmp, "%s/job_list.xml",
 
                    geninfo->config->datadir);
 
  writer = xmlNewTextWriterFilename(tmp, 0);
 
  free(tmp);
 

	
 
  xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL);
 

	
 
  /**
 
     create root element job_list
 
  */
 
  xmlTextWriterStartElement(writer, (xmlChar*)"job_list");
 

	
 
  _distren_asprintf(&tmp, "%d", geninfo->timestamp);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"timestamp", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  geninfo->total_priority_pieces = 0;
 
  counter = 0;
 
  for(job = geninfo->head.next; job; job = job->next)
 
    {
 
      _distren_asprintf(&tmp, "%d", job->jobnum);
 
      xmlTextWriterWriteElement(writer, (xmlChar*)"jobnum", (xmlChar*)tmp);
 
      free(tmp);
 

	
 
      /**
 
        this is needed for the new frame finder to work
 
      
 
        Why the random constant numeral 11? --ohnobinki
 
      */
 
      geninfo->total_priority_pieces = geninfo->total_priority_pieces + job->priority;
 

	
 
      counter++;
 
    }
 

	
 
  xmlTextWriterEndElement(writer);
 

	
 
  /**
 
     close elements and end document
 
  */
 
  xmlTextWriterEndDocument(writer);
 

	
 
  /**
 
     free writer and save xml file to disk
 
  */
 
  xmlFreeTextWriter(writer);
 
  return 0;
 
}
 

	
 
/* ************************** Test Functions ************************* */
 

	
 

	
 
/** Interactive test for the queuing system */
 
/* @QUEUE: Test uses methods not present in C code using mysql web-based system */
 
int interactiveTest(int test, struct general_info *geninfo)
 
{
 
  int command;
 
  int32_t slaveKey = 1;
 
  jobnum_t jobKey = 0;
 
  int32_t frameNum = 0;
 
  int32_t newPriority = 0;
 
  int tmp = 0;
 
  struct distrend_clientset *clients;
 

	
 
  fprintf(stderr,"Hello!\n");
 

	
 
  while(test == 1)
 
   {
 
     fprintf(stderr, "Welcome to DistRen Alpha Interactive Test Mode\n\n");
 
     fprintf(stderr, "\t1 \tGet a frame to render\n");
 
     fprintf(stderr, "\t2 \tChange job priority\n");
 
     fprintf(stderr, "\t3 \tSet frame finished\n");
 
     fprintf(stderr, "\t4 \tSet frame started\n");
 
     fprintf(stderr, "\t5 \tStart listener\n");
 
     fprintf(stderr, "\t0 \tQuit\n");
 

	
 
     scanf("%d", &command);
 

	
 
     switch(command)
 
     {
 
     case 1:
 
       fprintf(stderr,"Slave Key: ");
 
       scanf("%d", &slaveKey);
 
       fprintf(stderr, "Got frame: ");
 
       if(find_jobframe(geninfo->conn, slaveKey, &jobKey, &frameNum))
 
         fprintf(stderr,"No frames available to render!\n");
 
       else if(jobKey == -1)
 
         fprintf(stderr,"Slave %d has no render power!", slaveKey);
 
       else
 
         fprintf(stderr, "jobKey: %d, frameNum: %d\n",jobKey,frameNum);
 
       break;
 
     case 2:
 
       fprintf(stderr,"Job key: ");
 
       scanf("%d", &tmp);
 
       jobKey = tmp;
 

	
 
       fprintf(stderr,"New priority: ");
 
       scanf("%d", &tmp);
 
       newPriority = tmp;
 

	
 
       change_job_priority(geninfo->conn, jobKey, newPriority);
 
       fprintf(stderr,"Changed!");
 
       break;
 
     case 3:
 
       fprintf(stderr,"Slave Key: ");
 
       scanf("%d", &tmp);
 
       slaveKey = tmp;
 

	
 
       fprintf(stderr,"Job Key: ");
 
       scanf("%d", &tmp);
 
       jobKey = tmp;
 

	
 
       fprintf(stderr,"Frame Number: ");
 
       scanf("%d", &tmp);
 
       frameNum = tmp;
 

	
 
       finish_frame(geninfo->conn, slaveKey, jobKey, frameNum);
 
       fprintf(stderr,"Finished Frame!\n");
 
       break;
 
     case 4:
 
       fprintf(stderr,"Slave Key: ");
 
       scanf("%d", &tmp);
 
       slaveKey = tmp;
 

	
 
       fprintf(stderr,"Job Key: ");
 
       scanf("%d", &tmp);
 
       jobKey = tmp;
 

	
 
       fprintf(stderr,"Frame Number: ");
 
       scanf("%d", &tmp);
 
       frameNum = tmp;
 

	
 
       start_frame(geninfo->conn, slaveKey, jobKey, frameNum);
 
       fprintf(stderr,"Started Frame!\n");
 
       break;
 

	
 
     case 5:
 
       distrend_listen(geninfo->config, &clients);
 
       while(1)
 
	 {
 
	   distrend_accept(geninfo->config, clients);
 
	   /*
 
	     code for reading data from clients either goes here or in distrend_accept().
 
	     it might make sense for us to just pass the distrend_accept() function a
 
	     callback which can handle packets or to have a generic packet handling
 
	     subsystem which gathers data into groups defined by by packet.h and then
 
	     passed onto the callback.
 
	   */
 
	 }
 
       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
 
/*
 
  Copyright 2009 Nathan Phillip Brink
 
  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/>.
 

	
 
*/
 

	
 
struct distrend_config;
 

	
 
#ifndef _DISTREN_DISTREN_H_
 
#define _DISTREN_DISTREN_H_
 

	
 
#include <confuse.h>
 

	
 
#include "listen.h"
 

	
 
struct distrend_config
 
{
 
  cfg_t *mycfg;
 
  struct options_common *options;
 
  struct distrend_listen *listens; /*< Null terminated array of structs */
 
  char *datadir;
 

	
 
  int die;
 
};
 

	
 
#endif
src/server/distrenjob.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink <ohnobinki@ohnopublishing.net>
 
  Copyright 2010 Nathan Phillip Brink <ohnobinki@ohnopublishing.net>
 

	
 
  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 "distrenjob.h"
 
#include "slavefuncs.h"
 

	
 
#include "common/asprintf.h"
 

	
 
#include <libxml/parser.h>
 
#include <libxml/tree.h>
 
#include <libxml/xmlwriter.h>
 

	
 
void distrenjob_free(struct distrenjob **distrenjob)
 
{
 
  struct distrenjob *dj;
 

	
 
  dj = *distrenjob;
 
  xmlFree(dj->name);
 
  xmlFree(dj->submitter);
 

	
 
  free(dj->frameset);
 

	
 
  free(dj);
 
  *distrenjob = NULL;
 
}
 

	
 
int distrenjob_new(struct distrenjob **distrenjob)
 
{
 
  struct distrenjob *dj;
 

	
 
  dj = malloc(sizeof(struct distrenjob));
 
  if(!dj)
 
    {
 
      /* try to catch code that doesn't respect return values
 
       faster: */
 
      *distrenjob = NULL;
 
      return 1;
 
    }
 
  *distrenjob = dj;
 

	
 
  dj->next = NULL;
 
  dj->jobnum = 0; /*< @todo there should be a central jobnum allocator and a way to save the maximum jobnumber allocated */
 
  dj->type = 1;
 
  dj->name = (char *)NULL;
 
  dj->submitter = (char *)NULL;
 

	
 
  dj->output_format = (char *)NULL;
 
  dj->width = 0;
 
  dj->height = 0;
 

	
 
  dj->priority = 0;
 
  dj->completed_frames = 0;
 
  dj->assigned_frames = 0;
 
  dj->total_frames = 0;
 
  dj->prev_frame_index = -1;
 

	
 
  dj->total_render_time = 0;
 
  dj->assigned_render_power = 0;
 
  dj->watchdog_forgiveness = 3600; // initialize watchdog forgiveness at 1 hour
 
  dj->hibernate = 0;
 
  dj->frameset = (struct frameset *)NULL; /*< @todo does frameset need to be initialized here? */
 

	
 
  return 0;
 
}
 

	
 
/**
 
   read an unsigned integer property from an xmlNode
 

	
 
   convenience function for distrenjob_unserialize()
 
   @param xmlnode may be NULL for convenience
 
   @return 0 on success, other on error
 
 */
 
int _distrenjob_xml_readuint(xmlNodePtr xmlnode, xmlChar *propname, unsigned int *num)
 
{
 
  xmlChar *string;
 

	
 
  if(!xmlnode)
 
    return 1;
 

	
 
  string = xmlGetProp(xmlnode, propname);
 
  if(!string)
 
    {
 
      fprintf(stderr, "_distrenjob_xml_readuint(): warning: unable to get property ``%s''\n",
 
	      propname);
 
      return 1;
 
    }
 
  
 
  *num = (unsigned int)strtoul((char *)string, (char **)NULL, 10);
 

	
 
  xmlFree(string);
 
  return 0;
 
}
 

	
 
/**
 
   Loads a distrenjob that was serialized with distrenjob_serialize()
 

	
 
   @see distrenjob_serialize()
 
   @param pathtoxml path to XML file
 
   @param distrenjob will be set to a pointer
 
*/
 
int distrenjob_unserialize(struct distrenjob **distrenjob, char *pathtoxml)
 
{
 
  struct distrenjob *dj;
 
  struct frameset *fs;
 
  unsigned int start_frame;
 
  unsigned int end_frame;
 
  unsigned int counter;
 

	
 
  struct tm tm;
 

	
 
  xmlDocPtr xmldoc;
 
  xmlNodePtr xmlnode;
 
  xmlChar *xmlchar;
 

	
 
  xmlXPathContextPtr xmlxpathcontext;
 

	
 
  int tmp;
 

	
 
  if(distrenjob_new(distrenjob))
 
    return 1;
 
  dj = *distrenjob;
 

	
 
  xmldoc = xmlReadFile(pathtoxml, NULL, XML_PARSE_PEDANTIC);
 
  if(!xmldoc)
 
    {
 
      /**
 
	 @todo are we able to depend on libxml2's printed errors or
 
	 channel them into syslog output (eventually)? Currently,
 
	 this error is repetitious of a libxml2 error printed on stderr
 
	 for us.
 
       */
 
      fprintf(stderr, "error reading XML file ``%s''\n", pathtoxml);
 

	
 
      distrenjob_free(distrenjob);
 
      return 2;
 
    }
 

	
 
  xmlxpathcontext = xmlXPathNewContext(xmldoc);
 
  xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job");
 
  if(!xmlnode)
 
    {
 
      distrenjob_free(distrenjob);
 
      return 3;
 
    }
 

	
 
  /*< @todo should we use xmlChar everywhere too? */
 
  dj->name = (char *)xmlGetProp(xmlnode, (xmlChar *)"name");
 
  if(!dj->name)
 
    {
 
      distrenjob_free(distrenjob);
 
      return 4;
 
    }
 

	
 
  /*< @todo validation needs to be done on usernames. e.g., currently, they shouldn't contain the '"' char  */
 
  dj->submitter = (char *)xmlGetProp(xmlnode, (xmlChar *)"submitter");
 
  if(!dj->submitter)
 
    {
 
      distrenjob_free(distrenjob);
 
      return 5;
 
    }
 
  tmp = _distrenjob_xml_readuint(xmlnode, (xmlChar *)"priority", &dj->priority);
 
  if(tmp)
 
    {
 
      distrenjob_free(distrenjob);
 
      return 6;
 
    }
 
  
 
  dj->output_format = (char *)xmlGetProp(xmlnode, (xmlChar *)"output_format");
 
  if(!dj->output_format)
 
    {
 
      distrenjob_free(distrenjob);
 

	
 
      return 6;
 
    }
 

	
 
  tmp = _distrenjob_xml_readuint(xmlnode, (xmlChar *)"jobnum", &dj->jobnum);
 
  if(tmp)
 
    {
 
      distrenjob_free(distrenjob);
 
      
 
      return 7;
 
    }
 

	
 
  xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/resolution");
 
  tmp = _distrenjob_xml_readuint(xmlnode, (xmlChar *)"width"   , &dj->width   );
 
  tmp += _distrenjob_xml_readuint(xmlnode, (xmlChar *)"height"  , &dj->height  );
 

	
 
  xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/video");
 
  tmp += _distrenjob_xml_readuint(xmlnode, (xmlChar *)"start_frame", &start_frame);
 
  tmp += _distrenjob_xml_readuint(xmlnode, (xmlChar *)"end_frame", &end_frame);
 

	
 
  if(tmp)
 
    {
 
      fprintf(stderr, "distrenjob_unserialize(): error reading integer values from ``%s''\n",
 
	      pathtoxml);
 
      distrenjob_free(distrenjob);
 
      return 8;
 
    }
 

	
 
  /**
 
     total_render_time and watchdog stuff doesn't need error
 
     checking, just good defaults
 
  */
 
  xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/stats");
 
  dj->total_render_time = 0;
 
  xmlchar = xmlGetProp(xmlnode, (xmlChar *)"total_render_time");
 
  if(xmlchar)
 
    {
 
      if(strptime((char *)xmlchar, "%D %T", &tm))
 
	dj->total_render_time = mktime(&tm);
 
      xmlFree(xmlchar);
 
    }
 

	
 
  xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/watchdog");
 
  dj->watchdog_forgiveness = 3600;
 
  tmp =_distrenjob_xml_readuint(xmlnode, (xmlChar *)"forgiveness", &dj->watchdog_forgiveness);
 
  if(tmp)
 
    fprintf(stderr, "distrenjob_unserialize(): warning: watchdog forgiveness is unspecified in ``%s'', defaulting to 3600\n", pathtoxml);
 

	
 
  xmlXPathFreeContext(xmlxpathcontext);
 
  xmlFreeDoc(xmldoc);
 

	
 
  /**
 
     reconstruct the frameset
 
  */
 
  dj->total_frames = end_frame - start_frame + 1;
 
#ifndef NDEBUG
 
  fprintf(stderr, "distrenjob_unserialize(): restoring %d frames\n", dj->total_frames);
 
#endif
 
  dj->frameset = malloc(sizeof(struct frameset) * dj->total_frames);
 
  if(!dj->frameset)
 
    {
 
      fprintf(stderr, "OOM!\n");
 
      distrenjob_free(distrenjob);
 
      return 9;
 
    }
 
  fs = dj->frameset;
 
  for(counter = start_frame; counter <= end_frame; counter ++)
 
    {
 
      fs->num = counter;
 
      fs->status = FRAMESETSTATUS_UNASSIGNED; /*< @todo job partial completion and resumption support */
 

	
 
      fs ++;
 
    }
 
 
 
#ifndef NDEBUG
 
  fprintf(stderr, "distrenjob_unserialize(): finished loading ``%s''\n", pathtoxml);
 
#endif
 

	
 
  return 0;
 
}
 

	
 
int distrenjob_serialize(struct distrenjob *job, char *outfile)
 
{
 
  xmlTextWriterPtr writer;
 
  char *tmp;
 
  char *tmpfile;
 
  int tmprtn;
 

	
 
  /**
 
     transactional FS access for POSIX systems...
 
     (probably not implemented correctly)
 
   */
 
  _distren_asprintf(&tmpfile, "%s_", outfile);
 

	
 
  /* create xml document at the location tmp with no compression */
 
  writer = xmlNewTextWriterFilename(tmpfile, 0);
 
  xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL);
 

	
 
  /**
 
     write distrenjob element and add its attributes */
 
  xmlTextWriterStartElement(writer, (xmlChar*)"job");
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"name", (xmlChar*)job->name);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"submitter", (xmlChar*)job->submitter);
 
  _distren_asprintf(&tmp, "%d", job->priority);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"priority", (xmlChar*)tmp);
 
  free(tmp);
 
  _distren_asprintf(&tmp, "%d", job->jobnum);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar *)"jobnum", (xmlChar *)tmp);
 
  free(tmp);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar *)"output_format", (xmlChar *)job->output_format);
 
  
 

	
 
  /**
 
     write resolution element and add its attributes */
 
  xmlTextWriterStartElement(writer, (xmlChar*)"resolution");
 
  _distren_asprintf(&tmp, "%d", job->width);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"width", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  _distren_asprintf(&tmp, "%d", job->height);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"height", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  xmlTextWriterEndElement(writer);
 

	
 
  /**
 
     write video element and its attributes */
 
  xmlTextWriterStartElement(writer, (xmlChar*)"video");
 
  _distren_asprintf(&tmp, "%d", job->frameset[0].num);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"start_frame", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  _distren_asprintf(&tmp, "%d", job->frameset[ job->total_frames - 1 ].num);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"end_frame", (xmlChar*)tmp);
 
  free(tmp);
 

	
 
  xmlTextWriterWriteAttribute(writer, (xmlChar*)"output_format", (xmlChar*)job->output_format);
 
  xmlTextWriterEndElement(writer);
 

	
 
  /**
 
     write watchdog forgiveness element */
 
  xmlTextWriterStartElement(writer, (xmlChar*)"watchdog");
 

	
 
  _distren_asprintf(&tmp, "%d", job->watchdog_forgiveness);
 
  xmlTextWriterWriteAttribute(writer, (xmlChar *)"forgiveness", (xmlChar *)tmp);
 
  free(tmp);
 

	
 
  xmlTextWriterEndElement(writer); /* </watchdog> */
 

	
 
  xmlTextWriterEndElement(writer); /* </job> */
 

	
 
  /**
 
     end document */
 
  xmlTextWriterEndDocument(writer);
 

	
 
  /**
 
     free writer and save xml file to disk */
 
  xmlFreeTextWriter(writer);
 

	
 
  /**
 
     This is the key to transactioanl POSIX
 
     FS programming.
 
   */
 
  tmprtn = rename(tmpfile, outfile);
 
  if(tmprtn == -1)
 
    {
 
      fprintf(stderr, "%s:%d: Error renaming ``%s'' to ``%s''\n",
 
	      __FILE__, __LINE__,
 
	      tmpfile, outfile);
 
      perror("rename");
 
      tmprtn = 1;
 
    }
 
  free(tmpfile);
 

	
 
  return tmprtn;
 
}
src/server/distrenjob.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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_DISTRENJOB_H
 
#define _DISTREN_DISTRENJOB_H
 

	
 
/**
 
   This file stores the distrenjob and frameset structs and prototypes for some functions to manipulate/use these.
 
 */
 

	
 
#include <time.h> /*< clock_t, time_t */
 

	
 
typedef unsigned int jobnum_t;
 

	
 
/**
 
   Stores Blender Job Info
 
*/
 
struct distrenjob
 
{
 
  struct distrenjob *next; /*< next will be NULL unless if there is another distrenjob */
 
  jobnum_t jobnum;
 
  short int type; /*< 1:Blender, 2:something else */
 
  char *name;
 
  char *submitter;
 

	
 
  char *output_format; /*< currently is the file extension of the request output format. @todo make this mime-type based/not a string */
 
  unsigned int width; /*< width in pixels of the frames to be rendered */
 
  unsigned int height; /*< height in pixels of the frames to be rendered */
 

	
 
  unsigned int priority;  /*< 1 is lowest, 10 is highest, 0 means the job is done */
 
  unsigned int completed_frames; /*< number of completed frames for stats/etc */
 
  unsigned int assigned_frames; /*< number of assigned frames (that are not yet completed) for stats/etc */
 
  unsigned int total_frames; /*< how many frames are in the animation for stats/etc (unassigned frames) */
 
  int prev_frame_index; /*< the position in the array of the last frame that was assigned */
 

	
 
  time_t total_render_time; /*< total seconds of time spent on all the completed frames */
 
  unsigned long int assigned_render_power;
 
  unsigned int watchdog_forgiveness; /*< how many seconds 'til the frame is re-assigned (if client computer crashes etc); */
 
  short int hibernate; /*< @todo DOCUMENT THIS */
 
  struct frameset *frameset;
 
};
 

	
 

	
 

	
 
/**
 
   Frameset Structure
 
*/
 
enum framesetstatus
 
  {
 
    FRAMESETSTATUS_CANCELED, /*< The use has canceled this frame */
 
    FRAMESETSTATUS_UNASSIGNED, /*< The frame has not been assigned */
 
    FRAMESETSTATUS_ASSIGNED, /*< The frame has been assigned */
 
    FRAMESETSTATUS_DONE /*< The frame has completed rendering and the slave has returned the product to me */
 
  };
 

	
 
struct frameset {
 
  int num; /*< frame number to render */
 
  char slave_name; /*< user that frame is assigned to */
 
  enum framesetstatus status; /*< status of frame, 0= unassigned, 1= taken, 2= done */
 
  clock_t start_time; /*< time the frame was started */
 
};
 

	
 
/*
 
  related functions
 
*/
 

	
 
/**
 

	
 
   @param distrenjob the address where we will store the pointer of a malloc()ed
 
   distrenjob struct.
 
   @param pathtoxml filename/pathname of the xml file to be read into a distrenjob struct
 
 */
 
int distrenjob_unserialize(struct distrenjob **distrenjob, char *pathtoxml);
 

	
 
/**
 
   serialize a distrenjob into XML
 
   @param distrenjob the address where we will store the pointer of a malloc()ed
 
   distrenjob struct.
 
   @param xmloutput filename/pathname of the xml file to be written
 
 */
 
int distrenjob_serialize(struct distrenjob *distrenjob, char *xmloutputpath);
 

	
 

	
 
/**
 
   support function for distrenjob_unserialize() to help cleaning up a
 
   struct distrenjob when it is incompletely initialized.
 
   Also acts as a general-purpose struct distrenjob free()er ;-)
 
 */
 
void distrenjob_free(struct distrenjob **distrenjob);
 

	
 
/**
 
   initializes an empty, pointless struct distrenjob. This
 
   DOES run malloc() for you. It could return nonzero on error,
 
   but there are no errors to have yet (except for nomem). This
 
   sets all char* to NULL.
 

	
 
   You have to initialize your own frameset.
 
 */
 
int distrenjob_new(struct distrenjob **distrenjob);
 

	
 
#endif
src/server/listen.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink
 
  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 "listen.h"
 

	
 
#include <errno.h>
 
#include <fcntl.h>
 
#include <list.h>
 
#include <malloc.h>
 
#include <netinet/in.h>
 
#include <stdio.h>
 
#include <string.h>
 
#include <sys/types.h>
 
#include <sys/select.h>
 
#include <sys/socket.h>
 
#include <unistd.h>
 

	
 
/* local */
 

	
 
struct distrend_clientset
 
{
 
  LIST *clients;
 

	
 
  int nfds;
 
};
 

	
 
struct distrend_packet
 
{
 
  size_t len;
 
  char *data;
 
};
 

	
 
int distrend_client_new(struct distrend_client **client, int sock, enum distrend_client_state state);
 
int distrend_client_free(struct distrend_client *client);
 
void distrend_packet_free(struct distrend_packet *packet);
 
int distrend_makesocknonblock(int sock);
 

	
 
int distrend_listen(struct distrend_config *config, struct distrend_clientset **clients)
 
{
 
  int tmp;
 

	
 
  struct sockaddr_in6 sockaddr =
 
    {
 
      .sin6_family = AF_INET6,
 
      .sin6_port = 0,
 
      .sin6_flowinfo = 0,
 
      .sin6_addr = IN6ADDR_ANY_INIT,
 
      .sin6_scope_id = 0
 
    };
 

	
 
  *clients = malloc(sizeof(struct distrend_clientset));
 

	
 
  (*clients)->clients = list_init();
 
  (*clients)->nfds = 1;
 

	
 
  sockaddr.sin6_port = htons(4050);
 

	
 
  config->listens->sock = socket(AF_INET6, SOCK_STREAM, 0);
 
  tmp = bind(config->listens->sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
 
  if(tmp == -1)
 
    {
 
      perror("bind");
 
      free(*clients);
 

	
 
      return 1;
 
    }
 

	
 
  tmp = listen(config->listens->sock, 1);
 
  if(tmp == -1)
 
    {
 
      perror("listen");
 
      free(*clients);
 
      
 
      return 1;
 
    }
 

	
 
  tmp = distrend_makesocknonblock(config->listens->sock);
 
  if(tmp)
 
    {
 
      free(*clients);
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 
int distrend_handleread(struct distrend_config *config,
 
		    struct distrend_client *client)
 
{
 
  struct distrend_packet *packet;
 
  ssize_t readlen;
 
  char buf[8192];
 

	
 
  packet = malloc(sizeof(struct distrend_packet));
 
  if(!packet)
 
    {
 
      fprintf(stderr, "OOM\n");
 
      return 1;
 
    }
 
  packet->len = 0;
 
  packet->data = NULL;
 

	
 
  readlen = read(client->sock, buf, sizeof(buf));
 
  if(readlen == -1)
 
    {
 
      perror("read");
 
      switch(errno)
 
	{
 
	case EINTR:
 
	case EAGAIN:
 
	  break;
 

	
 
	default:
 
	  client->state = DISTREND_CLIENT_DEAD;
 

	
 
	  free(packet);
 
	  return 1;
 
	}
 
    }
 
  if(readlen == 0)
 
    /* handle EOF */
 
    {
 
      client->state = DISTREND_CLIENT_DEAD;
 

	
 
      free(packet);
 
      return 1;
 
    }
 

	
 
  packet->len = readlen;
 
  packet->data = malloc(readlen);
 
  if(!packet->data)
 
    {
 
      fprintf(stderr, "OOM!\n");
 

	
 
      free(packet);
 
      return 1;
 
    }
 
  memcpy(packet->data, buf, readlen);
 

	
 
  return 0;
 
}
 

	
 
struct distrend_accept_fdset_prepare_data
 
{
 
  fd_set readfds;
 
  fd_set writefds;
 
  /**
 
     stores the highest FD number which is one less than the first
 
   arfgument to select(). So do select(maxfd + 1, ...)
 
  */
 
  int maxfd;
 
};
 
int distrend_accept_fdset_prepare(struct distrend_accept_fdset_prepare_data *data,
 
				  struct distrend_client *client)
 
{
 
  if(client->state == DISTREND_CLIENT_DEAD)
 
    return TRUE;
 

	
 
  if(client->sock > data->maxfd)
 
    data->maxfd = client->sock;
 
  FD_SET(client->sock, &data->readfds);
 
  /**
 
     Only select on an outgoing socket if we have something to
 
     say.
 
   */
 
  if(!q_empty(client->outmsgs))
 
    FD_SET(client->sock, &data->writefds);
 
  
 
  return TRUE;
 
}
 

	
 
struct distrend_accept_client_proc_data
 
{
 
  fd_set *fdset;
 
  struct distrend_config *config;
 
  enum {DISTREND_ACCEPT_CLIENT_READ, DISTREND_ACCEPT_CLIENT_WRITE} mode;
 
};
 
int distrend_accept_client_proc(struct distrend_accept_client_proc_data *data,
 
				struct distrend_client *client)
 
{
 
  struct distrend_packet *packet;
 
  ssize_t written_amount;
 

	
 
  if(client->state == DISTREND_CLIENT_DEAD)
 
    return TRUE;
 

	
 
  if(!FD_ISSET(client->sock, data->fdset))
 
    /** continue iteration through the list */
 
    return TRUE;
 

	
 
  fprintf(stderr, "%s:%d: My traversal says that sock %d is available for %sing\n",
 
	  __FILE__, __LINE__, client->sock,
 
	  (data->mode == DISTREND_ACCEPT_CLIENT_READ) ? "read" : "write");
 

	
 
  switch(data->mode)
 
    {
 
    case DISTREND_ACCEPT_CLIENT_WRITE:
 
      if(q_empty(client->outmsgs))
 
	return TRUE;
 

	
 
      packet = q_front(client->outmsgs);
 
      written_amount = write(client->sock, packet->data, packet->len);
 
      /**
 
	 Disconnect in case of write error.
 
       */
 
      if(written_amount == -1)
 
	{
 
	  perror("write");
 
	  /*
 
	    distrend_client_free();
 

	
 
	    Until liblist has the ability to delete nodes
 
	    during traversal, we'll have to free the client
 
	    during a client-list cleanup/pruning cycle.
 
	  */
 
	  client->state = DISTREND_CLIENT_DEAD;
 
	}
 
      if(packet->len == written_amount)
 
	{
 
	  q_dequeue(client->outmsgs);
 
	  distrend_packet_free(packet);
 
	}
 
      else
 
	{
 
	  /**
 
	     shifting seems the simplest solution.
 
	   */
 
	  packet->len -= written_amount;
 
	}
 
      break;
 

	
 
    case DISTREND_ACCEPT_CLIENT_READ:
 
      distrend_handleread(data->config, client);
 
      break;
 
    }
 

	
 
  /** continue iteration through the list */
 
  return TRUE;
 
}
 

	
 
int distrend_accept(struct distrend_config *config, struct distrend_clientset *clients)
 
{
 
  int tmp;
 
  int newclientsock;
 

	
 
  struct distrend_accept_fdset_prepare_data fdsets;
 
  struct distrend_accept_client_proc_data travinfo;
 
  struct distrend_client *newclient;
 

	
 
  FD_ZERO(&fdsets.readfds);
 
  FD_ZERO(&fdsets.writefds);
 
  FD_SET(config->listens->sock, &fdsets.readfds);
 
  fdsets.maxfd = config->listens->sock;
 

	
 
  list_traverse(clients->clients, &fdsets, (list_traverse_func_t)&distrend_accept_fdset_prepare, LIST_FRNT | LIST_ALTR);
 

	
 
  fprintf(stderr, "select()...");
 
  tmp = select(fdsets.maxfd + 1, &fdsets.readfds, &fdsets.writefds, NULL, (struct timeval *)NULL);
 
  if(tmp == -1)
 
    {
 
      perror("select");
 

	
 
      return 1;
 
    }
 

	
 
  /**
 
     Deal first with data waiting to be sent and then
 
     with input data.
 
   */
 
  travinfo.config = config;
 

	
 
  travinfo.fdset = &fdsets.writefds;
 
  travinfo.mode = DISTREND_ACCEPT_CLIENT_WRITE;
 
  list_traverse(clients->clients, &travinfo, (list_traverse_func_t)&distrend_accept_client_proc, LIST_FRNT | LIST_ALTR);
 

	
 
  travinfo.fdset = &fdsets.readfds;
 
  travinfo.mode = DISTREND_ACCEPT_CLIENT_READ;
 
  list_traverse(clients->clients, &travinfo, (list_traverse_func_t)&distrend_accept_client_proc, LIST_FRNT | LIST_ALTR);
 

	
 
  /**
 
     Handle new connections.
 
   */
 
  if(FD_ISSET(config->listens->sock, &fdsets.readfds))
 
    {
 
      newclientsock = accept(config->listens->sock, (struct sockaddr *)NULL, (socklen_t *)NULL);
 
      if(distrend_client_new(&newclient, newclientsock, DISTREND_CLIENT_PREAUTH))
 
	{
 
	  fprintf(stderr, "error allocating client struct\n");
 
	  return 1;
 
	}
 
      clients->nfds ++;
 
      list_insert_after(clients->clients, newclient, 0);
 
      fprintf(stderr, "accepted new connection\n");
 
      distrend_client_write(newclient, "hello\n", 6);
 
    }
 

	
 
  /**
 
     Until liblist supports deleting nodes during a traversal,
 
     we clean dead clients here:
 
   */
 
  list_mvfront(clients->clients);
 
  newclient = list_curr(clients->clients);
 
  while(newclient)
 
    {
 
      if(newclient->state == DISTREND_CLIENT_DEAD)
 
	{
 
	  distrend_client_free(newclient);
 
	  list_remove_curr(clients->clients);
 
	  fprintf(stderr, "removed dead connection\n");
 
	}
 
      list_mvnext(clients->clients);
 
      /* provide for termination of this loop */
 
      if(newclient == list_curr(clients->clients))
 
	newclient = NULL;
 
      else
 
	newclient = list_curr(clients->clients);
 
    }
 

	
 
  return 0;
 
}
 

	
 
int distrend_unlisten(struct distrend_listen *listens, struct distrend_clientset *clients)
 
{
 
  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\n", __FILE__, __LINE__);
 
}
 

	
 
int distrend_client_new(struct distrend_client **client, int sock, enum distrend_client_state state)
 
{
 
  int tmp;
 

	
 
  tmp = distrend_makesocknonblock(sock);
 
  if(tmp)
 
    {
 
      fprintf(stderr, "Not accepting this connection because I cannot make the sock non-blocking\n");
 
      return 1;
 
    }
 

	
 
  *client = malloc(sizeof(struct distrend_client));
 
  if(!*client)
 
    {
 
      fprintf(stderr, "OOM\n");
 
      return 1;
 
    }
 
  (*client)->sock = sock;
 
  (*client)->state = state;
 
  (*client)->inmsgs = q_init();
 
  (*client)->outmsgs = q_init();
 

	
 
  return 0;
 
}
 

	
 
int distrend_client_free(struct distrend_client *client)
 
{
 
  q_free(client->inmsgs, (list_dealloc_func_t)distrend_packet_free);
 
  q_free(client->outmsgs, (list_dealloc_func_t)distrend_packet_free);
 

	
 
  close(client->sock);
 
  
 
  return 0;
 
}
 

	
 
void distrend_packet_free(struct distrend_packet *packet)
 
{
 
  free(packet->data);
 
  free(packet); 
 
}
 

	
 
int distrend_makesocknonblock(int sock)
 
{
 
  int fdstatus;
 
  int tmp;
 

	
 
  fdstatus = fcntl(sock, F_GETFL);
 
  fdstatus |= O_NONBLOCK;
 
  tmp = fcntl(sock, F_SETFL, fdstatus);
 
  if(tmp == -1)
 
    {
 
      perror("fcntl");
 
      return 1;
 
    }
 
  
 
  return 0;
 
}
 

	
 

	
 
int distrend_client_write(struct distrend_client *client, char *towrite, size_t msglen)
 
{
 
  struct distrend_packet *packet;
 

	
 
  packet = malloc(sizeof(struct distrend_packet));
 
  if(!packet)
 
    {
 
      fprintf(stderr, "OOM\n");
 
      return 1;
 
    }
 

	
 
  packet->len = msglen;
 
  packet->data = malloc(msglen);
 
  if(!packet->data)
 
    {
 
      free(packet);
 
      fprintf(stderr, "OOM\n");
 
      return 1;
 
    }
 

	
 
  memcpy(packet->data, towrite, msglen);
 

	
 
  q_enqueue(client->outmsgs, packet, 0);
 

	
 
  return 0;
 
}
 

	
 
int distrend_client_read(struct distrend_client *client, char **toread, size_t *lenread)
 
{
 
  struct distrend_packet *packet;
 

	
 
  *lenread = 0;
 
  *toread = NULL;
 

	
 
  if(q_empty(client->inmsgs))
 
    return 1;
 

	
 
  packet = q_dequeue(client->inmsgs);
 
  *lenread = packet->len;
 
  *toread = packet->data;
 
  free(packet);
 

	
 
  return 0;
 
}
src/server/listen.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink
 
  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/>.
 
*/
 

	
 
struct distrend_clientset;
 
struct distrend_listen;
 
struct distrend_client;
 

	
 
#ifndef _DISTREN_LISTEN_H
 
#define _DISTREN_LISTEN_H
 

	
 
#include "distrend.h"
 

	
 
#include <queue.h>
 

	
 
enum distrend_client_state
 
  {
 
    DISTREND_CLIENT_PREAUTH,
 
    DISTREND_CLIENT_GOODDOGGY,
 
    DISTREND_CLIENT_BADBOY,
 
    DISTREND_CLIENT_DEAD
 
  };
 

	
 
struct distrend_listen
 
{
 
  int port;
 
  int sock;
 
};
 

	
 
struct distrend_client
 
{
 
  int sock;
 
  enum distrend_client_state state;
 
  QUEUE *inmsgs;
 
  QUEUE *outmsgs;
 
};
 

	
 

	
 

	
 
/**
 
   initializes the listens and clientset
 
   @param config the configuration from distrend
 
   @param clients a pointer to a struct distrend_clientset pointer which will be set to memory allocated for the clientset
 
 */
 
int distrend_listen(struct distrend_config *config, struct distrend_clientset **clients);
 

	
 
/**
 
   checks states of the sockets I'm managing. If there are any new connections,
 
   or activity on any sockets, I'll call the appropriate function.
 
   I will block until there is some sort of activity, including
 
   signals. If you want to cleanly shut down, it's best to register
 
   signal handlers somewhere
 
 */
 
int distrend_accept(struct distrend_config *config, struct distrend_clientset *clients);
 

	
 
/**
 
   cleans listening socket. Unnecessary for a working server, currently a stub.
 
 */
 
int distrend_unlisten(struct distrend_listen *listens, struct distrend_clientset *clients);
 

	
 
/**
 
   writes message to client.
 
   @param towrite the caller is expected to free this string. This function will
 
   strdup() it, in essence.
 
 */
 
int distrend_client_write(struct distrend_client *client, char *towrite, size_t msglen);
 

	
 
/**
 
   This is probably just NOT a placeholder for remotio
 
*/
 
void remotio_send_to_client();
 

	
 
#endif
src/server/mysql.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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 "mysql.h"
 
#include <mysql/mysql.h>
 

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

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

	
 
/**
 
   local types
 
 */
 

	
 
#define SEVENTY_FIVE 75
 
#define FORTY_TWO 42
 

	
 
/**
 
   performs mysql query.
 
   errors will be logged to the user by this function.
 
   @return pointer to query handle on success, NULL on failure or if expected_columns=0 (no result set expected)
 
 */
 
distrend_mysql_result_t mysqlQuery(distrend_mysql_conn_t conn, char *query, MYSQL_FIELD_OFFSET expected_columns);
 

	
 
/**
 
   frees mysql query result. Accepts a NULL pointer and ignores it to
 
   help deal with one-shot calls to mysqlQuery so that you don't have to
 
   check if it returned NULL or not.
 
   @return 0 on success
 
 */
 
int mysqlResultFree(distrend_mysql_conn_t conn, distrend_mysql_result_t result);
 

	
 
/**
 
   reads an integer mysql field value
 
   @return 0 on success
 
*/
 
int distrend_mysql_getint(MYSQL_ROW row, MYSQL_FIELD_OFFSET column, int32_t *theint)
 
{
 
  if(!row[column])
 
    return 1;
 

	
 
  *theint = atol(row[column]);
 

	
 
  return 0;
 
}
 

	
 

	
 
struct distrend_mysql_conn
 
{
 
  MYSQL *mysqlconn;
 
  short pointlesscheck;
 
};
 

	
 
struct distrend_mysql_result
 
{
 
  MYSQL_RES *mysqlresult;
 
  short pointlesscheck;
 
};
 

	
 
/**
 
    funcs
 
 */
 

	
 
int mysqlConnect(distrend_mysql_conn_t *conn)
 
{
 
  MYSQL *mysqlconn;
 

	
 
  char *server = "zserver1";
 
  char *user = "distren";
 
  char *password = "HwLxuBqTvJ6J7FWj";
 
  char *database = "distren";
 

	
 
  my_bool mybool;
 

	
 
  mysqlconn = mysql_init(NULL);
 

	
 
  if(!mysql_real_connect(mysqlconn, server, user, password, database, 0, NULL, CLIENT_MULTI_STATEMENTS))
 
    {
 
      fprintf(stderr, "%s\n", mysql_error(mysqlconn));
 
      return 1;
 
    }
 

	
 
  /**
 
     Called after mysql_real_connect() due to bug fixed in MySQL 5.1.6 and later
 
  */
 
  mybool = 1;
 
  mysql_options(mysqlconn, MYSQL_OPT_RECONNECT, (char *)&mybool);
 

	
 

	
 
  *conn = malloc(sizeof(struct distrend_mysql_conn));
 
  if(!*conn)
 
    {
 
      mysql_close(mysqlconn);
 
      mysql_server_end();
 
      return 2;
 
    }
 
  (*conn)->mysqlconn = mysqlconn;
 
  (*conn)->pointlesscheck = SEVENTY_FIVE;
 

	
 
  return 0;
 
}
 

	
 
int mysqlDisconnect(distrend_mysql_conn_t conn)
 
{
 
  /**
 
     check if this handle is valid
 
   */
 
  if(conn->pointlesscheck != SEVENTY_FIVE)
 
    fprintf(stderr, "warning, I was passed a bad struct distrend_mysql_conn...\n");
 

	
 
  /**
 
     invalidate handle :-D
 
   */
 
  conn->pointlesscheck ++;
 

	
 
  mysql_close(conn->mysqlconn);
 
  mysql_server_end();
 
  free(conn);
 

	
 
  return 0;
 
}
 

	
 

	
 
distrend_mysql_result_t mysqlQuery(distrend_mysql_conn_t conn, char *query, MYSQL_FIELD_OFFSET expected_columns)
 
{
 
  MYSQL_RES *result;
 
  distrend_mysql_result_t distrenresult;
 
  MYSQL_FIELD_OFFSET num_columns;
 

	
 
  /**
 
     pointless sanity check
 
  */
 
  if(conn->pointlesscheck != SEVENTY_FIVE)
 
    fprintf(stderr, "warning, I was passed a bad struct distrend_mysql_conn...\n");
 

	
 
  /** make sure that connection is still alive
 
   */
 
  
 
  if(mysql_ping(conn->mysqlconn))
 
    {
 
      fprintf(stderr, "mysql_ping() failed: %s\n", mysql_error(conn->mysqlconn));
 
      return NULL;
 
    }
 

	
 
  fprintf(stderr,"Querying... ");
 
  if (mysql_query(conn->mysqlconn, query))
 
    {
 
      fprintf(stderr, "calling mysql_query() resulted in: %s\n", mysql_error(conn->mysqlconn));
 
      return NULL;
 
    }
 
  fprintf(stderr,"Queried!\n");
 

	
 
  fprintf(stderr,"Getting results... ");
 
  result = mysql_use_result(conn->mysqlconn);
 
  
 
  /**
 
     Flush stuff out when an empty set is expected.
 
   */
 
  if(!expected_columns)
 
    {
 
      if(result)
 
	{
 
	  while(mysql_fetch_row(result))
 
	    ;
 
	  mysql_free_result(result);
 
	}
 
      return NULL;
 
    }
 

	
 
  if(!result)
 
    {
 
      fprintf(stderr, "expected response/result for query ``%s'', got nothing\n", query);
 
      return NULL;
 
    }
 

	
 
  /**
 
     Sanity check
 
   */
 
  num_columns = mysql_num_fields(result);
 
  if(num_columns != expected_columns)
 
    {
 
      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");
 

	
 
  /**
 
     Prepare data for return.
 
   */
 
  distrenresult = malloc(sizeof(struct distrend_mysql_result));
 
  if(!distrenresult)
 
    {
 
      while(mysql_fetch_row(result))
 
	;
 
        mysql_free_result(result);
 
      return NULL;
 
    }
 

	
 
  distrenresult->mysqlresult = result;
 
  distrenresult->pointlesscheck = FORTY_TWO;
 

	
 
  return distrenresult;
 
}
 

	
 
int mysqlResultFree(distrend_mysql_conn_t conn, distrend_mysql_result_t result)
 
{
 
  size_t counter;
 
  MYSQL_RES *mysqlresult;
 

	
 
  if(!result)
 
    {
 
      fprintf(stderr, "mysqlResultFree(): warning, passed NULL parameter\n");
 
      return 0;
 
    }
 
  
 
  if(result->pointlesscheck != FORTY_TWO)
 
    fprintf(stderr, "%s:%d: I didn't get the type of handle I wanted\n", __FILE__, __LINE__);
 

	
 
  mysqlresult = result->mysqlresult;
 
  free(result);
 

	
 
  /**
 
     Must flush the resultset buffer.
 
   */
 
  for(counter = 0;
 
      mysql_fetch_row(mysqlresult);
 
      counter ++)
 
    ;
 
  if(counter)
 
    fprintf(stderr, "Calling function did not flush %d rows\n", (int)counter);
 

	
 
  while(mysql_more_results(conn->mysqlconn))
 
    {
 
      fprintf(stderr, "flushing an extraneous result set\n");
 
      mysql_next_result(conn->mysqlconn);
 
      mysqlresult = mysql_use_result(conn->mysqlconn);
 
      if(mysqlresult)
 
	{
 
	  while(mysql_fetch_row(mysqlresult))
 
	    ;
 
	  mysql_free_result(mysqlresult);
 
	}
 
    }
 

	
 
  return 0;
 
}
 

	
 
/*
 
   Individual query functions:
 
 */
 

	
 
void finish_frame(distrend_mysql_conn_t conn, int32_t slavekey, int32_t jobkey, int32_t framenum)
 
{
 
  char *query;
 

	
 
  _distren_asprintf(&query, "CALL `distren`.`Frame_Complete`(%d,%d,%d);", slavekey, jobkey, framenum);
 

	
 
  mysqlQuery(conn, query, 0);
 
  free(query);
 
}
 

	
 
void start_frame(distrend_mysql_conn_t conn, int32_t slavekey, int32_t jobkey, int32_t framenum)
 
{
 
  char *query;
 

	
 
  _distren_asprintf(&query, "CALL `distren`.`Frame_Start`(%d,%d,%d);", slavekey, jobkey, framenum);
 

	
 
  mysqlQuery(conn, query, 0);
 
  free(query);
 
}
 

	
 
void set_renderpower(distrend_mysql_conn_t conn, int32_t slavekey, int32_t renderpower)
 
{
 
  char *query;
 

	
 
  _distren_asprintf(&query, "CALL `distren`.`Slave_Render_Power_Update`(%d,%d);", slavekey, renderpower);
 

	
 
  mysqlQuery(conn, query, 0);
 
  free(query);
 
}
 

	
 
int change_job_priority(distrend_mysql_conn_t conn, int32_t jobkey, int32_t newpriority)
 
{
 
  char *query;
 

	
 
  _distren_asprintf(&query, "UPDATE `distren`.`Job` SET `Priority`=%d WHERE `Job_Key`=%d",
 
		    newpriority, jobkey);
 
  mysqlQuery(conn, query, 0);
 

	
 
  return 0;
 
}
 

	
 
int find_jobframe(distrend_mysql_conn_t conn, int32_t slavekey, jobnum_t *jobkey, int32_t *framenum)
 
{
 
  distrend_mysql_result_t result;
 
  char *query;
 
  MYSQL_ROW row;
 

	
 
  int rtn;
 

	
 
  _distren_asprintf(&query, "CALL `distren`.`Frame_Get`(%d);", slavekey);
 

	
 
  result = mysqlQuery(conn, query, 2);
 
  free(query);
 

	
 
  if(!result)
 
    return 1;
 

	
 
  row = mysql_fetch_row(result->mysqlresult);
 
  if(!row)
 
    {
 
      mysqlResultFree(conn, result);
 
      return 1;
 
    }
 

	
 
  rtn = distrend_mysql_getint(row, 0, (int32_t *)jobkey);
 
  rtn += distrend_mysql_getint(row, 1, framenum);
 
  
 
  mysqlResultFree(conn, result);
 

	
 
  return rtn;
 
}
 

	
 
int auth_slave(distrend_mysql_conn_t conn, int32_t slavekey, char *slavepass)
 
{
 
  // Return values:
 
  // -- 2-user doesn't exist
 
  // -- 1-pass is wrong
 
  // -- 0-parameters correct
 

	
 
  distrend_mysql_result_t result;
 
  char *query;
 
  MYSQL_ROW row;
 
  int32_t *isAuth = 0;
 

	
 
  int rtn;
 
  mysql_real_escape_string(conn->mysqlconn,slavepass,slavepass,(unsigned int)strlen(slavepass));
 

	
 
  _distren_asprintf(&query, "CALL `distren`.`Slave_Check`(%d,'%s');", slavekey, slavepass);
 

	
 
  result = mysqlQuery(conn, query, 2);
 
  free(query);
 

	
 
  if(!result)
 
    return 1;
 

	
 
  row = mysql_fetch_row(result->mysqlresult);
 
  if(!row)
 
    {
 
      mysqlResultFree(conn, result);
 
      return 1;
 
    }
 

	
 
  rtn = distrend_mysql_getint(row, 0, (int32_t *)isAuth);
 
  
 
  mysqlResultFree(conn, result);
 

	
 
  return rtn;
 
}
src/server/mysql.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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 MYSQL_H_
 
#define MYSQL_H_
 

	
 
#include "distrenjob.h"
 

	
 
#include <stdint.h>
 

	
 
struct distrend_mysql_conn;
 
struct distrend_mysql_result;
 

	
 
typedef struct distrend_mysql_conn *distrend_mysql_conn_t;
 
typedef struct distrend_mysql_result *distrend_mysql_result_t;
 

	
 
/**
 
   initiates a MySQL connection
 
   @param conn, pointer will be set to the struct
 
   @return 0 on success
 
 */
 
int mysqlConnect(distrend_mysql_conn_t *conn);
 

	
 
/**
 
   cleans and disconnects MySQL connection
 
   @param conn connection to clean
 
   @return 0 on success
 
*/
 
int mysqlDisconnect(distrend_mysql_conn_t conn);
 

	
 
/**
 
   Mark a frame as finished in the database and calculate the seconds spent on that frame.
 
 */
 
void finish_frame(distrend_mysql_conn_t conn, int32_t slavekey, int32_t jobkey, int32_t framenum);
 

	
 
/**
 
   Mark a frame as started in the database and save the time at which it started.
 
 */
 
void start_frame(distrend_mysql_conn_t conn, int32_t slavekey, int32_t jobkey, int32_t framenum);
 

	
 
/**
 
   Mark a frame as started in the database and save the time at which it started.
 
*/
 
void set_renderpower(distrend_mysql_conn_t conn, int32_t slavekey, int32_t renderpower);
 

	
 
/**
 
   Changes the priority of an existing (and maybe running) job. @arg head I may end up changing the head if job == head
 
*/
 
int change_job_priority(distrend_mysql_conn_t conn, int32_t jobkey, int32_t newpriority);
 

	
 
/**
 
  Frame Finder: Finds a frame for a slave to render
 
*/
 
int find_jobframe(distrend_mysql_conn_t conn, int32_t slaveKey, jobnum_t *jobKey, int32_t *frameNum);
 

	
 
/**
 
  Auth Slave: Checks username/password of slave
 
*/
 
int auth_slave(distrend_mysql_conn_t conn, int32_t slavekey, char *slavepass);
 

	
 

	
 

	
 
#endif /* MYSQL_H_ */
src/server/simpleslave.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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 "slavefuncs.h"
 

	
 
#include "common/asprintf.h"
 
#include "common/options.h"
 

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

	
 
#define DEBUG 1
 

	
 
int main(int argc, char *argv[])
 
{
 
  char *datadir;
 
  char *server;
 
  char *username;
 
  char *password;
 
  char *hostname;
 

	
 
  cfg_opt_t myopts[] = {
 
    CFG_SIMPLE_STR("username", &username),
 
    CFG_SIMPLE_STR("password", &password),
 
    CFG_SIMPLE_STR("datadir", &datadir),
 
    CFG_SIMPLE_STR("server", &server),
 
    CFG_SIMPLE_STR("hostname", &hostname),
 
    CFG_END()
 
  };
 
  cfg_t * my_cfg;
 

	
 
  struct options_common *commonopts;
 

	
 
  /**
 
     initializations
 
  */
 
  datadir = NULL;
 
  server = NULL;
 
  username = NULL;
 
  password = NULL;
 
  hostname = NULL;
 

	
 
  char curopt;
 

	
 
  while(((char)-1) != (curopt = getopt(argc, argv, "u:th")))
 
     {
 
       if(curopt == ':')
 
         {
 
           fprintf(stderr, "-%c: is missing an argument\n", optopt);
 
           return 1;
 
         }
 
       else if(curopt == '?')
 
         {
 
           fprintf(stderr, "-%c: invalid option specified\n", optopt);
 
           return 1;
 
         }
 
       else if(curopt == 'h')
 
         {
 
           fprintf(stderr, "Usage: distrenslave [option] \nStarts a distren slave\n\t-u\tset username (run after fresh install)\n\t-t\tEnter test mode\n\t-h\tshow this help\n");
 
           return 2;
 
         }
 
       else if(curopt == 't')
 
         {
 
           slaveTest();
 
           return 0;
 
         }
 
       else if(curopt == 'u')
 
         username = strdup(optarg);
 
         fprintf(stderr, "Putting username \"%s\" in distrenslave.conf\n", username);
 
         conf_replace("distrenslave.conf", "!username", username);
 
         fprintf(stderr, "Please invoke distrenslave with no arguments to run with the username you just set\n");
 
         return 0;
 
     }
 

	
 
  /* Get conf data */
 
  options_init(argc, argv, &my_cfg, myopts, "slave", &commonopts);
 

	
 
  if(!datadir)
 
    {
 
      fprintf(stderr, "datadir not set\n");
 
      return 1;
 
    }
 
  if(!server)
 
    {
 
      fprintf(stderr, "server not set\n");
 
      return 1;
 
    }
 
  if(!username)
 
    {
 
      fprintf(stderr, "username not set\n");
 
      return 1;
 
    }
 
  if(!password)
 
    {
 
      fprintf(stderr, "password not set\n");
 
      return 1;
 
    }
 
  if(!hostname)
 
    {
 
      fprintf(stderr, "hostname not set\n");
 
      return 1;
 
    }
 

	
 
  
 

	
 
  /* Notifies the user if there no username in .conf */
 
  if(checkUsername(username))
 
    return 1;
 
  if(!strncmp(password, "!password",10))
 
    {
 
      fprintf(stderr, "You haven't specified a password. Please edit your distrenslave.conf file appropriately\n");
 
      return 1;
 
    }
 

	
 
  // 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 */
 
  char *pathtoTar;     /* Full path to the location of the job#.tgz */
 
  char *pathtoTardir;
 

	
 
  // Temporary for uncomressed testing
 
  char *urltoJobfile;
 

	
 
  char *urltoOutput;   /* Full URL where output is posted */
 
  char *pathtoOutput;  /* Full path to the output (rendered) file */
 
  char *pathtoOutdir;  /* Full path to output directory */
 
  char *pathtoRenderOutput; /* Contains blender framenum placeholder */
 

	
 
  char *pathtoJob; /* 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,"DistRenSimpleSlave Pre-Alpha\n\t- this version of DistRen is still experimental\n\t  so things might go horribly wrong!\n");
 

	
 
  int benchmarkTime = 0;
 
  int renderPower = 0;
 

	
 
  char option[1] = "";
 
  fprintf(stderr,"Do you want to recalculate render power? (y/n): ");
 
  scanf("%s", option);
 
  if(!strcmp(option, "y"))
 
    {
 
      if(slaveBenchmark(datadir, &benchmarkTime, &renderPower))
 
	{
 
	  fprintf(stderr,"Benchmark failed! Exiting.\n");
 
	  return 1;
 
	}
 
      else
 
	{
 
	  fprintf(stderr,"Benchmark successful, time taken was %d seconds, giving you a render power of %d.\n",
 
		  benchmarkTime, renderPower);
 
	  _web_setrenderpower(slavekey, password, renderPower);
 
	}
 
    }
 
  if(!DEBUG)
 
    fprintf(stderr, "Running..");
 
  // Main loop
 
  while(!quit)
 
    {
 

	
 
    // request work
 
    if(DEBUG)
 
      fprintf(stderr,"Requesting work from %s...\n", hostname);
 
    haveWork = _web_getwork(slavekey, password, &jobnum, &framenum);
 

	
 
    /* If we got a frame */
 
    if(haveWork)
 
      {
 
        fprintf(stderr,"Got work from server...\n");
 
        /* @TODO: Add remotio hooks */
 
        // jobnum = remoteio_read(jobnum); /* Set jobnum from remoteio (we could use info from struct, but we need this info to download the xmlfile */
 
        // framenum = remoteio_read(jobnum); /* Set framenum from remoteio */
 
        // 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);
 
        mkdir(pathtoTardir, 0700);
 
        // int dlret = downloadTar(urltoTar, pathtoTar);
 
        int dlret = downloadTar(urltoJobfile, pathtoJobfile);
 
        if(dlret == 0)
 
          fprintf(stderr,"Data retrieved successfully!\n");
 
        else if(dlret == 3){
 
          _web_resetframe(slavekey, password, 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 uncompressed data %s...\n", pathtoJobfile);
 
          //fprintf(stderr,"Using existing tarball %s...\n", pathtoTar);
 

	
 
        /* // Commented for decompression
 
        struct stat jbuffer;
 
        int jstatus = stat(pathtoJobfile, &jbuffer);
 
        if(jstatus == -1){
 
          fprintf(stderr,"Main job file does not exist, extracting...\n");
 

	
 
          // If error unpacking tarball
 
          if(unpackJob(pathtoJob, pathtoTar)){
 
            _web_resetframe(slavekey, password, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
            return 1;
 
          }
 
        }
 
        */
 

	
 
        /* ignore return because directory may exist already */
 
        if(DEBUG)
 
          fprintf(stderr,"Creating output directory %s\n", pathtoOutdir);
 
        mkdir(pathtoOutdir, 0700);
 

	
 
        if(DEBUG)
 
          fprintf(stderr,"Marking frame started on server... ");
 
        _web_startframe(slavekey, password, jobnum, framenum);
 

	
 
        /* Execute blender */
 
        if(DEBUG){
 
          fprintf(stderr,"Executing blender on file %s\n", pathtoJobfile);
 
          fprintf(stderr,"Directing output to file %s\n", pathtoOutput);
 
        }
 

	
 
        if(exec_blender(pathtoJobfile, pathtoRenderOutput, framenum))
 
          {
 
            fprintf(stderr,"Error running Blender. Check your installation and/or your PATH.\n");
 
            _web_resetframe(slavekey, password, 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);
 
          _web_resetframe(slavekey, password, 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);
 
          else
 
            fprintf(stderr,"Finished.\n");
 
          uploadOutput(pathtoOutput, urltoOutput, jobnum, framenum, slavekey); // @TODO: Handle return value
 

	
 
          free(urltoOutput);
 
          free(pathtoOutput);
 
          urltoOutput = NULL;
 
          pathtoOutput = NULL;
 
          // Tell the server that rendering and upload are complete
 
          _web_finishframe(slavekey, password, jobnum, framenum);
 
        }
 
      }
 
    else{
 
      if(DEBUG)
 
        fprintf(stderr,"No work to do. Idling...\n");
 
      else
 
        fprintf(stderr,".");
 
      sleep(300); // Poll every 300 seconds @TODO: remove polling
 
    }
 

	
 
    // @TODO: If the server says that every frame for the last jobnum is finished, OR if the data is getting old
 
    if(1 == 0)
 
      {
 
        // Note: individual frames are already deleted after uploading,
 
        // except for ones that couldn't be uploaded
 
        delete_jobdata(jobnum, datadir);
 
      }
 
  }
 

	
 
  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");
 
  return 0;
 
}
src/server/slave.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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 "slavefuncs.h"
 

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

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

	
 

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

	
 
  char *datadir;
 
  char *server;
 
  char *username;
 
  char *hostname;
 
  cfg_opt_t myopts[] = {
 
    CFG_SIMPLE_STR("username", &username),
 
    CFG_SIMPLE_STR("datadir", &datadir),
 
    CFG_SIMPLE_STR("server", &server),
 
    CFG_SIMPLE_STR("hostname", &hostname),
 
    CFG_END()
 
  };
 
  cfg_t * my_cfg;
 

	
 
  struct options_common *commonopts;
 

	
 
  struct distrenjob *myjob; /* Structure to hold data gathered from the XML file */
 

	
 
  struct remoteio *comm_slave;
 

	
 
  /**
 
     initializations
 
  */
 
  datadir = NULL;
 
  server = NULL;
 
  username = NULL;
 

	
 
  char curopt;
 

	
 
  while(((char)-1) != (curopt = getopt(argc, argv, "u:th")))
 
     {
 
       if(curopt == ':')
 
         {
 
           fprintf(stderr, "-%c: is missing an argument\n", optopt);
 
           return 1;
 
         }
 
       else if(curopt == '?')
 
         {
 
           fprintf(stderr, "-%c: invalid option specified\n", optopt);
 
           return 1;
 
         }
 
       else if(curopt == 'h')
 
         {
 
           fprintf(stderr, "Usage: distrenslave [option] \nStarts a distren slave\n\t-u\tset username (run after fresh install)\n\t-t\tEnter test mode\n\t-h\tshow this help\n");
 
           return 2;
 
         }
 
       else if(curopt == 't')
 
         {
 
           slaveTest();
 
           return 0;
 
         }
 
       else if(curopt == 'u')
 
         username = strdup(optarg);
 
         fprintf(stderr, "Putting username \"%s\" in distrenslave.conf\n", username);
 
         conf_replace("distrenslave.conf", "!username", username);
 
         fprintf(stderr, "Please invoke distrenslave with no arguments to run with the username you just set\n");
 
         return 0;
 
     }
 

	
 
  /* Get conf data */
 
  options_init(argc, argv, &my_cfg, myopts, "slave", &commonopts);
 

	
 

	
 
  /* Notifies the user if there no username in .conf */
 
  if(checkUsername(username))
 
    return 1;
 

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

	
 

	
 
  // 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 */
 
  char *pathtoTar;     /* Full path to the location of the job#.tgz */
 
  char *pathtoTardir;
 
  char *pathtoRenderOutput;
 

	
 
  char *urltoOutput;   /* Full URL where output is posted */
 
  char *pathtoOutput;  /* Full path to the output (rendered) file */
 
  char *pathtoOutdir;  /* Full path to output directory */
 

	
 
  // Temporary for uncomressed testing
 
  char *urltoJobfile;
 

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

	
 
  int haveWork = 0;
 
  int quit = 0;
 

	
 
  // Main loop
 
  while(!quit)
 
    {
 

	
 
    // request work
 
    fprintf(stderr,"Requesting work...\n");
 
    sendSignal(comm_slave, DISTREN_REQUEST_GETWORK);
 

	
 
    /* If we got a frame */
 
    if(haveWork)
 
      {
 
        fprintf(stderr,"Got work from server...\n");
 
        /* @TODO: Add remotio hooks */
 
        // jobnum = remoteio_read(jobnum); /* Set jobnum from remoteio (we could use info from struct, but we need this info to download the xmlfile */
 
        // framenum = remoteio_read(jobnum); /* Set framenum from remoteio */
 
        // outputExt = remotio)read(outputExt); /* Set output extension from remotio */
 

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

	
 
        if(downloadTar(urltoTar, pathtoTar))
 
          return 1;
 

	
 
        unpackJob(datadir, pathtoTar);
 

	
 
        /* Execute blender */
 
        if(exec_blender(pathtoJobfile, pathtoOutput, framenum))
 
          {
 
            fprintf(stderr,"Error running Blender. Check your installation and/or your PATH.\n");
 
            return 1;
 
          }
 
        free(pathtoJobfile);
 

	
 
        /* Post-execution */
 
        fprintf(stderr, "Finished frame %d in job %d, uploading...\n", framenum, jobnum);
 
        uploadOutput(pathtoOutput, urltoOutput, jobnum, framenum, slavekey); // @TODO: Handle return value
 

	
 
        free(urltoOutput);
 
        free(pathtoOutput);
 

	
 
        // Tell the server that rendering and upload are complete of "jobjum.framenum"
 
	char *jobFrame;
 
	_distren_asprintf(&jobFrame, "%s.%s", jobnum, framenum);
 
        sendExtSignal(comm_slave, DISTREN_REQUEST_DONEFRAME, jobFrame);
 
	free(jobFrame);
 
      }
 
    else
 
      fprintf(stderr,"Nothing to do. Idling...\n");
 

	
 
    // @TODO: If the server says that every frame for the last jobnum is finished, OR if the data is getting old
 
    if(1 == 0)
 
      {
 
        // Note: individual frames are already deleted after uploading,
 
        // except for ones that couldn't be uploaded
 
        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,"Goodbye!\n");
 
  return 0;
 
}
src/server/slavefuncs.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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 "distrenjob.h"
 
#include "slavefuncs.h"
 

	
 
#include "common/asprintf.h"
 
#include "common/execio.h"
 
#include "common/protocol.h"
 
#include "common/remoteio.h"
 

	
 
#include <curl/curl.h>
 
#include <curl/easy.h>
 
#include <curl/types.h>
 

	
 
#include <archive.h>
 
#include <archive_entry.h>
 

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

	
 
#include <time.h>
 

	
 
/**
 
   Sends the server a single request (see protocol.h)
 
*/
 
int sendSignal(struct remoteio *rem, char signal)
 
{
 
  size_t written;
 
  size_t towrite;
 
  char *ssignal;
 

	
 
  _distren_asprintf(&ssignal, "%c", signal);
 
  towrite = strlen(ssignal);
 
  while( towrite
 
	 && !remoteio_write(rem, ssignal, towrite, &written))
 
    {
 
      fprintf(stderr, "Sending request...\n");
 
      towrite -= written;
 
    }
 
  if(written)
 
    return 0;
 

	
 
  /**
 
     if remoteio_write returned 1, the connection
 
     is probably dead or there was a real error
 
   */
 
  return 1;
 
}
 

	
 
/**
 
   Sends the server an extended signal (request + data)
 
*/
 
int sendExtSignal(struct remoteio *rem, char signal, char *data){
 
  size_t written;
 
  size_t towrite;
 
  char *ssignal;
 
  _distren_asprintf(&ssignal, "%c%s", signal, data); // Just append the data FIXME: We should do this differently
 
  towrite = strlen(ssignal);
 
  while( towrite
 
          && !remoteio_write(rem, ssignal, towrite, &written))
 
     {
 
       fprintf(stderr, "Sending request...\n");
 
       towrite -= written;
 
     }
 
   if(written)
 
     return 0;
 

	
 
   /**
 
      if remoteio_write returned 1, the connection
 
      is probably dead or there was a real error
 
    */
 
   return 1;
 
}
 

	
 

	
 

	
 
/**
 
   Grabs the xml DOM node reached by an XPath.
 

	
 
   @param path an XPath that lead to DOM node
 
   @return the first node associated with the path or NULL if there is no match
 
 */
 
xmlNodePtr xml_quickxpath(xmlXPathContextPtr xpathctxt, xmlChar *path)
 
{
 
  xmlNodePtr toreturn;
 

	
 
  xmlXPathObjectPtr xmlxpathobjptr;
 
  xmlxpathobjptr = xmlXPathEval(path, xpathctxt);
 
  if(!xmlxpathobjptr
 
     || !xmlxpathobjptr->nodesetval->nodeNr)
 
    {
 
      fprintf(stderr, "XPath resolution failed for ``%s'' in ``%s'' (``%s'')\n", path, xpathctxt->doc->name, xpathctxt->doc->URL);
 
      return (xmlNodePtr)NULL;
 
    }
 

	
 
  toreturn = *(xmlxpathobjptr->nodesetval->nodeTab);
 

	
 
  xmlXPathFreeObject(xmlxpathobjptr);
 

	
 
  return toreturn;
 
}
 

	
 

	
 
/** Stub for deleting job data from the disk. @TODO: unstubify me! */
 
int delete_jobdata(int jobnum, char *datadir)
 
{
 
  char *jobpath;
 
  _distren_asprintf(&jobpath, "%s/%d", datadir, jobnum);
 
  // rmdir(jobpath);
 
  fprintf(stderr, "Please manually remove %s. Automatic removal is currently not implemented.\n", jobpath);
 
  return 0;
 
}
 

	
 
/** Function referenced by curlget() to write data to disk. */
 
size_t curl_writetodisk(void *ptr, size_t size, size_t nmemb, FILE *stream)
 
 {
 
    return fwrite(ptr, size, nmemb, stream);
 
  }
 

	
 
/** Helper function for cURL's progress display */
 
int curl_progress(char *Bar, double t, double d, double ultotal, double ulnow)
 
{
 
fprintf(stderr,"Downloading: %f%% complete\r",d/t*100);
 
return 0;
 
}
 

	
 
/** Retrieves a URL with cURL and saves it to disk */
 
CURLcode curlget(char *url, char *out){
 
  fprintf(stderr,"Preparing to download %s\n",url);
 
  double *Bar; // Stores cURL progress display info
 
  CURL *curl;
 
  CURLcode res;
 
  FILE *outfile;
 

	
 
  res = CURLE_FAILED_INIT;
 
  curl = curl_easy_init();
 
  if(curl)
 
    {
 
    outfile = fopen(out, "w"); // Open where we're writing to
 
    if(outfile == NULL)
 
      return 2; // Output dir doesn't exist
 
    curl_easy_setopt(curl, CURLOPT_URL, url);
 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_writetodisk); // this MUST be set for win32 compat.
 
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
 
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, (curl_progress_callback)&curl_progress);
 
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &Bar);
 
    res = curl_easy_perform(curl);
 
    curl_easy_cleanup(curl);
 
    fclose(outfile);
 
  }
 
  fprintf(stderr,"\n"); // Clears out he progressbar's carriage return
 
  return res; // 0 is OK, 1 is 404 or other error
 
}
 

	
 
/** Posts a file to a url with cUrl */
 
CURLcode curlpost(char *filename, char *url, int jobnum, int framenum, int slavekey)
 
{
 
  char *targetname = "uploadedfile"; // Name of the target in the php file on the server (Don't change me unless you have different PHP code)
 
  CURL *curl;
 
  CURLcode res;
 
  struct curl_httppost *formpost=NULL;
 
  struct curl_httppost *lastptr=NULL;
 
  struct curl_slist *headerlist=NULL;
 
  static const char buf[] = "Expect:";
 

	
 
  char *sjobnum;
 
  char *sframenum;
 
  char *sslavekey;
 

	
 
  fprintf(stderr,"Uploading to %s\n", url);
 

	
 
  _distren_asprintf(&sjobnum,"%d",jobnum);
 
  _distren_asprintf(&sframenum,"%d",framenum);
 
  _distren_asprintf(&sslavekey,"%d",slavekey);
 

	
 
  curl_global_init(CURL_GLOBAL_ALL);
 

	
 
  /* jobnum field... */
 
   curl_formadd(&formpost,
 
                &lastptr,
 
                CURLFORM_COPYNAME, "jobnum",
 
                CURLFORM_COPYCONTENTS, sjobnum, CURLFORM_END);
 
  /* framenum field... */
 
  curl_formadd(&formpost,
 
               &lastptr,
 
               CURLFORM_COPYNAME, "framenum",
 
               CURLFORM_COPYCONTENTS, sframenum, CURLFORM_END);
 
  /* slavekey field... */
 
  curl_formadd(&formpost,
 
               &lastptr,
 
               CURLFORM_COPYNAME, "slavekey",
 
               CURLFORM_COPYCONTENTS, sslavekey, CURLFORM_END);
 
  /* upload field... */
 
  curl_formadd(&formpost,
 
               &lastptr,
 
               CURLFORM_COPYNAME, targetname,
 
               CURLFORM_FILE, filename,
 
               CURLFORM_END);
 
  /* filename field... */
 
  curl_formadd(&formpost,
 
               &lastptr,
 
               CURLFORM_COPYNAME, "filename",
 
               CURLFORM_COPYCONTENTS, filename,
 
               CURLFORM_END);
 
  /* submit field, not usually needed, just in case... */
 
  curl_formadd(&formpost,
 
               &lastptr,
 
               CURLFORM_COPYNAME, "submit",
 
               CURLFORM_COPYCONTENTS, "send",
 
               CURLFORM_END);
 

	
 
  res = CURLE_FAILED_INIT;
 
  curl = curl_easy_init();
 
  headerlist = curl_slist_append(headerlist, buf);
 
  if(curl)
 
    {
 
    /* Setting the URL to get the post, and the contents of the post */
 
    curl_easy_setopt(curl, CURLOPT_URL, url);
 
    curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
 
    res = curl_easy_perform(curl);
 

	
 
    curl_easy_cleanup(curl);
 
    /* cleanup the formpost junk */
 
    curl_formfree(formpost);
 
    curl_slist_free_all (headerlist);
 
    free(sjobnum);
 
    free(sframenum);
 
    free(sslavekey);
 
    }
 
  return res;
 
}
 

	
 

	
 
/** Logs the user into the server after ensuring that keys exist */
 
int login_user(char *username)
 
{
 
  // @TODO: Put some telnet-style auth code here unless this is obselete
 
  return 1; // success
 
}
 

	
 
/** Replaces wordtoreplace with replacewith in conffile (relative to SYSCONFDIR) */
 
int conf_replace(char *conffile, char *wordtoreplace, char *replacewith){
 
  int maxlinelen = 120;
 
  char *fileOrig;
 
  char *fileRepl;
 
  _distren_asprintf(&fileOrig, "%s/%s", SYSCONFDIR, conffile);
 
  _distren_asprintf(&fileRepl, "%s%s.edited", SYSCONFDIR, conffile);
 
  char buffer[maxlinelen+2];
 
  char *buff_ptr, *find_ptr;
 
  FILE *fp1, *fp2;
 
  size_t find_len = strlen(wordtoreplace);
 
  fp1 = fopen(fileOrig,"r");
 
  fp2 = fopen(fileRepl,"w");
 
  if (fp1 ==NULL){
 
    fprintf(stderr, "%s doesn't exist\n",fileOrig);
 
    return 0;
 
  }
 
  else if(fp2 ==NULL){
 
    fprintf(stderr, "Can't write a file to disk! Check permissions.\n");
 
    return 0;
 
  }
 
  else{
 
    while(fgets(buffer,maxlinelen+2,fp1))
 
      {
 
        buff_ptr = buffer;
 
        while ((find_ptr = strstr(buff_ptr,wordtoreplace)))
 
        {
 
           while(buff_ptr < find_ptr)
 
             fputc((int)*buff_ptr++,fp2);
 
           fputs(replacewith,fp2);
 
           buff_ptr += find_len;
 
         }
 
         fputs(buff_ptr,fp2);
 
      }
 
    rename(fileRepl, fileOrig);
 
  }
 
  fclose(fp2);
 
  fclose(fp1);
 
  fprintf(stderr,"Wrote conf file...\n");
 
return 1; // Success
 
}
 

	
 

	
 
/* Executors */
 

	
 
/** Executor function for Blender operations */
 
int exec_blender(char *input, char *output, int frame)
 
{
 
  int ret;
 
  char *frame_str;
 
  _distren_asprintf(&frame_str, "%i", frame);
 

	
 
  char *command = "blender"; // @TODO: We currently expect this to be in PATH
 
  char *cmd[] = { command, "-b", input, "-o", output, "-f", frame_str, "-t", "0", (char *)NULL }; // arguments for blender
 

	
 
  fprintf(stderr,"Preparing to execute command: %s -b %s -o %s -f %s\n", command, input, output, frame_str);
 

	
 
  char buf[20];
 
  struct execio *testrem;
 
  size_t readlen;
 

	
 
  fprintf(stderr,"Executing: %s\n", frame_str);
 
  ret = execio_open(&testrem, command, cmd);
 
  if(ret)
 
    {
 
      fprintf(stderr, "error running blender\n");
 
      return 1;
 
    }
 
  buf[19] = '\0';
 
  while(!execio_read(testrem, buf, 19, &readlen))
 
    {
 
       buf[readlen] = '\0';
 
       fprintf(stderr, "read \"%s\"\n", buf);
 
    }
 
  execio_close(testrem);
 
  free(frame_str);
 
  return ret;
 
}
 

	
 
void xmlinit()
 
{
 
  xmlInitParser();
 
  xmlXPathInit();
 
}
 

	
 
void xmlcleanup()
 
{
 
  xmlCleanupParser();
 
}
 

	
 

	
 
/** Creates directories recursively */
 
int distren_mkdir_recurse(char *dirname)
 
{
 
  size_t counter;
 
  char *nextdir;
 

	
 
  nextdir = strdup(dirname);
 
  for(counter = 0; nextdir[counter]; counter ++)
 
    {
 
      /** @TODO OS-portabalize the path-separators */
 
      if(nextdir[counter] == '/')
 
        {
 
          nextdir[counter] = '\0';
 
          mkdir(nextdir, S_IRWXU | S_IRGRP | S_IROTH);
 
          nextdir[counter] = '/';
 
        }
 
    }
 

	
 
  return 0;
 
}
 

	
 
/**
 
 @TODO: Use for constructing path to job data locally and/or on data.distren.org
 
 */
 
int job_build_path(char *filename, unsigned int jobnum)
 
{
 
  return 0;
 
}
 

	
 

	
 
int downloadTar(char *url, char *destinationPath){
 
  // Prepare to download the job tar if it isn't  present
 
  struct stat buffer;
 
  int fstatus = stat(destinationPath, &buffer);
 
  if(fstatus == -1)
 
    {
 
      // Download the Tar
 
      int ret = curlget(url, destinationPath);
 
      if(ret == 0){
 
        fprintf(stderr, "Job data retrieved successfully\n");
 
        // free(url); @FIXME: causes doublefree! Curl must free the url?
 
        return 0;
 
      }
 
      else if(ret == 1){
 
        fprintf(stderr, "Downloading job data from %s failed. Check your network connection.\n",url);
 
        free(url);
 
        return 1; // Eventually make a retry loop
 
      }
 
    }
 
  fprintf(stderr, "Tar already exists! Download cancelled.\n");
 
  return 2;
 
}
 

	
 

	
 
int uploadOutput(char *pathtoOutput, char *urltoOutput, int jobnum, int framenum, int slavekey){
 
  //fprintf(stderr,"Uploading output %s to url %s for job %d and frame %d for slavekey %d", pathtoOutput, urltoOutput, jobnum, framenum, slavekey);
 
  if( !curlpost(pathtoOutput, urltoOutput, jobnum, framenum, slavekey)) // Uploads output
 
    {
 
      fprintf(stderr,"Upload successful, removing old output...\n");
 
      remove(pathtoOutput); // Delete the file after its uploaded
 
      return 0;
 
    }
 
  else
 
    {
 
      fprintf(stderr,"Upload failed. Check your network connection. Retrying upload...\n");
 
      int tries=1;
 
      while(tries<=10 && curlpost(pathtoOutput, urltoOutput, jobnum, framenum, slavekey))
 
        {
 
          fprintf(stderr, "Upload failed. Trying again in 10 seconds... (attempt %d of 10)\n", tries);
 
          tries++;
 
          sleep(10);
 
        }
 
      return 1; // Upload failed after multiple tries
 
      // @FUTURE: Keep track of files that we were unable to upload, and upload them later
 
    }
 
}
 

	
 
/**
 
   Extracts archive to the specified directory, creating this directory
 
   if necessary (but this directory will not be recursively created).
 
   @param outdir output directory
 
   @param pathtoTar filename of the archive to extract
 
 */
 
int unpackJob(char *outdir, char *pathtoTar)
 
{
 
  int ret;
 

	
 
  struct archive *a;
 
  struct archive_entry *ae;
 
  int astatus;
 

	
 
  /* ignore return because directory may exist already */
 
  mkdir(outdir, 0700);
 
  ret = chdir(outdir);
 
  if(ret == -1)
 
    {
 
      perror("chdir");
 
      return 1;
 
    }
 

	
 
  a = archive_read_new();
 
  ae = archive_entry_new();
 

	
 
  archive_read_support_compression_all(a);
 
  archive_read_support_format_raw(a);
 
  astatus = archive_read_open_filename(a, pathtoTar, 8192);
 
  if (astatus != ARCHIVE_OK)
 
    {
 
      fprintf(stderr, "error opening archive\n");
 
      return 1;
 
    }
 

	
 
  for(astatus = ARCHIVE_OK;
 
      astatus == ARCHIVE_OK
 
	|| astatus == ARCHIVE_WARN;
 
      )
 
    {
 
      astatus = archive_read_next_header2(a, ae);
 
      if(astatus == ARCHIVE_WARN)
 
	fprintf(stderr, "Encountered nonfatal read error somehow\n");
 

	
 
      if(astatus == ARCHIVE_OK
 
	 || astatus == ARCHIVE_WARN)
 
	astatus = archive_read_extract(a, ae,
 
				       ARCHIVE_EXTRACT_NO_OVERWRITE
 
				       | ARCHIVE_EXTRACT_SECURE_SYMLINKS
 
				       | ARCHIVE_EXTRACT_SECURE_NODOTDOT);
 
    }
 
  archive_entry_free(ae);
 
  archive_read_finish(a);
 
  
 
  if(astatus != ARCHIVE_EOF)
 
    {
 
      fprintf(stderr, "Error reading archive\n");
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 

	
 
void prepareJobPaths(int jobnum, int framenum, char *outputExt, char *datadir, char **urltoTar,char **pathtoTar,char **pathtoTardir,char **pathtoJob, char **pathtoJobfile, char **urltoJobfile, char **urltoOutput,char **pathtoOutput, char **pathtoRenderOutput, char **pathtoOutdir)
 
{
 
  // Variable Preparation
 
  char *jobdatapath;
 
   _distren_asprintf(&jobdatapath, "job%d", jobnum);
 
   _distren_asprintf(urltoTar, "http://data.distren.org/job%d/job%d.tar.gz", jobnum, jobnum); // Prepares URL to download from
 
   _distren_asprintf(urltoJobfile, "http://data.distren.org/job%d/job.blend", jobnum); // Prepares URL to download from
 

	
 
   _distren_asprintf(pathtoTar, "%s/%s/job%d.tar.gz", datadir, jobdatapath, jobnum); // Prepares destination to save to
 

	
 
   _distren_asprintf(pathtoTardir, "%s/%s/", datadir, jobdatapath); // Prepares destination to save to
 

	
 
   _distren_asprintf(pathtoJob, "%s/%s/", datadir, jobdatapath);
 

	
 
   _distren_asprintf(pathtoJobfile, "%s/%s/job.blend", datadir, jobdatapath ); // Prepares the path to the jobfile
 
   _distren_asprintf(urltoOutput, "http://dev.distren.org/slaveUpload.php?jobkey=%d&framekey=%d",jobnum,framenum); // Prepares the URL where output is posted
 

	
 
   _distren_asprintf(pathtoRenderOutput, "%s/%s/output/job%d-frame#.%s", datadir, jobdatapath, jobnum, outputExt ); // Note: the # is for blender to put in framenum in output file
 
   _distren_asprintf(pathtoOutput, "%s/%s/output/job%d-frame%d.%s", datadir, jobdatapath, jobnum, framenum, outputExt ); // Note: the # is for blender to put in framenum in output file
 

	
 
   _distren_asprintf(pathtoOutdir, "%s/%s/output", datadir, jobdatapath);
 
   free(jobdatapath);
 
}
 

	
 
int checkUsername(char *username)
 
{
 
  if(username == NULL || strcmp(username, "!username") == 0 )
 
    {
 
     fprintf(stderr, "\nPlease ensure that your username is present in distrenslave.conf\n");
 
     return 1;
 
   }
 
   else
 
     if( username != NULL || strcmp(username, "!username") != 0 )
 
       {
 
         // Log in the user
 
         if(login_user(username) == 1){
 
           fprintf(stderr, "You should now be logged into distren.\n");
 
           return 0;
 
         }
 
         else
 
           {
 
             fprintf(stderr, "Login failed, please check your username. If you have not registered, please do so on the DistRen website.\n");
 
             return 1;
 
           }
 
       }
 
     else
 
       {
 
         fprintf(stderr, "Please check your distrenslave.conf, it appears to be incorrectly formatted.\n");
 
         return 1;
 
       }
 
}
 

	
 
void slaveTest(char *datadir)
 
{
 
  int command;
 
  int test = 1;
 
  int jobnum = 0;
 
  int framenum = 0;
 
  int slavekey = 0;
 
  fprintf(stderr,"Hello!\n");
 
  char tmpString1[100] = "";
 
  char tmpString2[100] = "";
 
  char *uploadURL = "http://distren.org/slaveUpload.php";
 

	
 
  while(test == 1)
 
   {
 
     fprintf(stderr, "Welcome to DistRenSlave Alpha Interactive Test Mode\n\n");
 
     fprintf(stderr, "\t1 \tTest posting (uploading) data\n");
 
     fprintf(stderr, "\t2 \tTest getting frame from server\n");
 
     fprintf(stderr, "\t3 \tTest data downloading\n");
 
     fprintf(stderr, "\t4 \tTest archive decompression\n");
 
     fprintf(stderr, "\t5 \tQuit\n");
 

	
 
     scanf("%d", &command);
 

	
 
     switch(command)
 
     {
 
     case 1:
 
       fprintf(stderr,"Path to output file to upload: ");
 
       scanf("%99s", tmpString1);
 
       fprintf(stderr,"Framenum that is being uploaded: ");
 
       scanf("%d", &framenum);
 
       fprintf(stderr,"Jobnum that frame belongs to: ");
 
       scanf("%d", &jobnum);
 
       fprintf(stderr,"Slavekey that rendered this output: ");
 
       scanf("%d", &slavekey);
 
       uploadOutput(tmpString1, uploadURL, jobnum, framenum, slavekey);
 
       break;
 
     case 2:
 
       fprintf(stderr,"Remotio not implemented yet!\n");
 
       break;
 
     case 3:
 
       fprintf(stderr,"URL to file: ");
 
       scanf("%s", tmpString1);
 
       fprintf(stderr,"Local save path (including filename): ");
 
       scanf("%s", tmpString2);
 
       if(downloadTar(tmpString1, tmpString2))
 
         fprintf(stderr,"Error downloading!\n");
 
       else
 
         fprintf(stderr,"Success!\n");
 
       break;
 
     case 4:
 
       fprintf(stderr,"Jobnum to decompress: ");
 
       scanf("%d", &jobnum);
 
       fprintf(stderr,"Path to compressed data: ");
 
       scanf("%s", tmpString2);
 
       unpackJob(datadir, tmpString1);
 
       break;
 
     case 5:
 
       test = 0;
 
       break;
 
     default:
 
       fprintf(stderr, "Invalid input, please try again.\n");
 
       break;
 
     }
 
   }
 
}
 

	
 
/*
 
int runBenchmark(int slavekey, char *datadir){
 
  // Execute blender
 

	
 
  if(exec_blender(pathtoJobfile, pathtoOutput, framenum))
 
    {
 
      fprintf(stderr,"Error running Blender. Check your installation and/or your PATH.\n");
 
      _web_resetframe(slavekey, password, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
      return 1;
 
    }
 
  free(pathtoJobfile);
 

	
 
  struct stat buffer;
 
  int fstatus = stat(pathtoOutput, &buffer);
 
  if(fstatus == -1){
 
    fprintf(stderr,"*** Frame was not rendered correctly! Scene may not have camera, or your blender installation is not working.\n");
 
    _web_resetframe(slavekey, password, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
    return 1;
 
  }
 

	
 
}
 
*/
 

	
 
/* simpleSlave functions */
 

	
 
/** Memory struct for curl */
 
struct _web_memorystruct {
 
  char *memory;
 
  size_t size;
 
};
 

	
 
void *_web_myrealloc(void *ptr, size_t size);
 

	
 
void *_web_myrealloc(void *ptr, size_t size)
 
{
 
  /* There might be a realloc() out there that doesn't like reallocing
 
     NULL pointers, so we take care of it here */
 
  if(ptr)
 
    return realloc(ptr, size);
 
  else
 
    return malloc(size);
 
}
 

	
 
size_t _web_writememorycallback(void *ptr, size_t size, size_t nmemb, void *data)
 
{
 
  size_t realsize = size * nmemb;
 
  struct _web_memorystruct *mem = (struct _web_memorystruct *)data;
 

	
 
  mem->memory = _web_myrealloc(mem->memory, mem->size + realsize + 1);
 
  if (mem->memory) {
 
    memcpy(&(mem->memory[mem->size]), ptr, realsize);
 
    mem->size += realsize;
 
    mem->memory[mem->size] = 0;
 
  }
 
  return realsize;
 
}
 

	
 
struct _web_memorystruct _web_getrequest(char *url){
 
  // fprintf(stderr,"Preparing to get request at %s",url);
 

	
 
  CURL *curl;
 
  CURLcode res;
 
  struct _web_memorystruct chunk;
 

	
 
  chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
 
  chunk.size = 0;    /* no data at this point */
 

	
 
  curl = curl_easy_init();
 
  if(curl) {
 
    curl_easy_setopt(curl, CURLOPT_URL, url);
 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _web_writememorycallback);
 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
 
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
 

	
 
    res = curl_easy_perform(curl);
 
    curl_easy_cleanup(curl);
 
  }
 

	
 
  /* we're done with libcurl, so clean it up */
 
  curl_global_cleanup();
 
  return chunk; // 0 is OK, 1 is 404 or other error
 
}
 

	
 
void _web_finishframe(int slavekey, char *slavepass, int jobnum, int framenum){
 
  char *url;
 
  _distren_asprintf(&url,"http://dev.distren.org/slave/act.php?mode=finishframe&slavekey=%d&slavepass=%s&jobnum=%d&framenum=%d", slavekey, slavepass, jobnum, framenum);
 
  struct _web_memorystruct data = _web_getrequest(url);
 
  free(url);
 

	
 
  fprintf(stderr,"%s\n", data.memory);
 
  if(data.memory)
 
    free(data.memory);
 
}
 

	
 
void _web_resetframe(int slavekey, char *slavepass, int jobnum, int framenum){
 
  fprintf(stderr,"Resetting frame %d in job %d on server... ",framenum,jobnum);
 
  char *url;
 
  _distren_asprintf(&url,"http://dev.distren.org/slave/act.php?mode=resetframe&slavekey=%d&slavepass=%s&jobnum=%d&framenum=%d", slavekey, slavepass, jobnum, framenum);
 
  struct _web_memorystruct data = _web_getrequest(url);
 
  free(url);
 

	
 
  fprintf(stderr,"%s\n", data.memory);
 
  if(data.memory)
 
    free(data.memory);
 
}
 

	
 
void _web_startframe(int slavekey, char *slavepass, int jobnum, int framenum){
 
  fprintf(stderr,"Marking frame %d started on server... ",framenum);
 
  char *url;
 
  _distren_asprintf(&url,"http://dev.distren.org/slave/act.php?mode=startframe&slavekey=%d&slavepass=%s&jobnum=%d&framenum=%d", slavekey, slavepass, jobnum, framenum);
 
  struct _web_memorystruct data = _web_getrequest(url);
 
  free(url);
 

	
 
  fprintf(stderr,"%s\n", data.memory);
 
  if(data.memory)
 
    free(data.memory);
 
}
 

	
 
int _web_getwork(int slavekey, char *slavepass, int *jobnum, int *framenum){
 
  char *url;
 
  _distren_asprintf(&url,"http://dev.distren.org/slave/act.php?mode=getwork&slavekey=%d&slavepass=%s", slavekey, slavepass);
 
  struct _web_memorystruct data = _web_getrequest(url);
 
  free(url);
 

	
 
  if(!data.memory || !strcmp(data.memory,",")){
 
    fprintf(stderr,"No work available on server!\n");
 
    return 0;
 
  }
 
  else if(!strcmp(data.memory, "ERROR_BADKEY")){
 
    fprintf(stderr,"*** Slave %d does not exist!\n",slavekey);
 
    free(data.memory);
 
    return 0;
 
  }
 
  // Compare to PACKAGE_VERSION
 
  else{
 
    char *tmp;
 
    char *serverversion;
 

	
 
    tmp = strtok (data.memory,",,");
 
    if(tmp != NULL){ // make sure work is available
 
      *jobnum = atoi(tmp);
 
      tmp = strtok (NULL, ",");
 
      if(tmp == NULL)
 
        return 0; // no work
 
      *framenum = atoi(tmp);
 
      tmp = strtok (NULL, ",");
 
      if(tmp == NULL)
 
        return 0; // no work
 
      serverversion = tmp;
 
      if(strcmp(PACKAGE_VERSION,serverversion)){
 
        fprintf(stderr,"Your distren package is out of date! Please acquire a newer version. (%s local vs %s remote)\n", PACKAGE_VERSION, serverversion);
 
        return 0;
 
      }
 
      free(data.memory);
 
      return 1;
 
    }
 
    else
 
      return 0; // error
src/server/slavefuncs.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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_SLAVEFUNCS_H
 
#define _DISTREN_SLAVEFUNCS_H
 

	
 
#include "distrenjob.h"
 

	
 
#include "common/remoteio.h"
 

	
 
#include <curl/curl.h>
 
#include <libxml/xpath.h>
 
#include <stdio.h>
 

	
 

	
 
struct msg;
 
int sendSignal(struct remoteio *rem, char signal);
 
int sendExtSignal(struct remoteio *rem, char signal, char *data);
 
xmlNodePtr xml_quickxpath(xmlXPathContextPtr xpathctxt, xmlChar *path);
 
int software_updatecheck();
 
int delete_jobdata(int jobnum, char *datadir);
 
size_t curl_writetodisk(void *ptr, size_t size, size_t nmemb, FILE *stream);
 
CURLcode curlget(char *url, char *out);
 
CURLcode curlpost(char *filename, char *url, int jobnum, int framenum, int slavekey);
 
int ssh_keygen();
 
int register_user(char *username, char *email);
 
int login_user(char *username);
 
int conf_replace(char *conffile, char *wordtoreplace, char *replacewith);
 
int exec_blender(char *input, char *output, int frame);
 
void xmlinit();
 
void xmlcleanup();
 
int distren_mkdir_recurse(char *dirname);
 
int job_build_path(char *filename, unsigned int jobnum);
 
int downloadTar(char *url, char *destinationPath);
 
int uploadOutput(char *pathtoOutput, char *urltoOutput, int jobnum, int framenum, int slavekey);
 
int unpackJob(char *outdir, char *pathtoTar);
 
void prepareJobPaths(int jobnum, int framenum, char *outputExt, char *datadir, char **urltoTar,char **pathtoTar,char **pathtoTardir,char **pathtoJob, char **pathtoJobfile,char **urltoJobfile,char **urltoOutput,char **pathtoOutput, char **pathtoRenderOutput, char **pathtoOutdir);
 
int checkUsername(char *username);
 
void slaveTest();
 

	
 
/* Simple slave */
 
struct _web_memorystruct;
 
void *_web_myrealloc(void *ptr, size_t size);
 
size_t _web_writememorycallback(void *ptr, size_t size, size_t nmemb, void *data);
 
struct _web_memorystruct _web_getrequest(char *url);
 
void _web_finishframe(int slavekey, char *slavepass, int jobnum, int framenum);
 
void _web_startframe(int slavekey, char *slavepass, int jobnum, int framenum);
 
void _web_resetframe(int slavekey, char *slavepass, int jobnum, int framenum);
 
int _web_getwork(int slavekey, char *slavepass, int *jobnum, int *framenum);
 
void _web_setrenderpower(int slavekey, char *slavepass, int renderpower);
 
int slaveBenchmark(char *datadir, int *benchmarkTime, int *renderPower);
 

	
 
#endif
src/server/user_mgr.c
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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 "user_mgr.h"
 

	
 
#include "common/asprintf.h"
 

	
 

	
 
#include <libxml/xmlmemory.h>
 
#include <libxml/parser.h>
 
#include <libxml/tree.h>
 
#include <libxml/encoding.h>
 
#include <libxml/xmlwriter.h>
 
#include <libxml/xmlreader.h>
 

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

	
 
struct user_mgr_info
 
{
 
	struct user *user_array;
 
	int current_users;
 
	int user_array_size;
 
} user_mgr_info;
 

	
 
int resize_user_array()
 
{
 
	int counter;
 
	int counter2;
 

	
 
	// create array twice the size of the current amount of users
 
	user_mgr_info.user_array_size = user_mgr_info.current_users * 2;
 
	struct user *new_user_array = malloc(sizeof(struct user) * user_mgr_info.user_array_size);
 

	
 
	// this copies the original user_array over to the new one
 
	// using two counters allows the array to be resized at any time
 
	// leaving exactly 1 open space between each user when it is done;
 
	counter2 = 0;
 
	for(counter = 0; counter < user_mgr_info.current_users; counter++)
 
	{
 
		if(user_mgr_info.user_array[counter].username != 0)
 
		{
 
			new_user_array[counter2*2] = user_mgr_info.user_array[counter];
 
			counter2++;
 
		}
 
	}
 

	
 
	// cleanup old array
 
	free(user_mgr_info.user_array);
 

	
 
	// change the pointer to point to the new user array
 
	user_mgr_info.user_array = new_user_array;
 

	
 
	return 1;
 
}
 

	
 
struct user *findUser(char *nameOfUser)
 
{
 
	int high;
 
	int low;
 
	int middle;
 
	int result;
 
	high = user_mgr_info.user_array_size - 1;
 
	low = 0;
 

	
 
	for(middle = (low+high)/2; 1 == 1; middle = (low+high)/2)
 
	{
 
		// in case middle lands on a part of the array with no user
 
		while(user_mgr_info.user_array[middle].username == 0)
 
		{
 
			if(result < 0)
 
				middle--;
 
			else
 
				middle++;
 
		}
 

	
 
		// this is where the array is cut in half and the half that the nameOfUser is on is kept
 
		result = strcmp(nameOfUser, user_mgr_info.user_array[middle].username);
 
		if(result == 0)
 
			return &user_mgr_info.user_array[middle];
 
		else if(result < 0)
 
			high = middle;
 
		else
 
			low = middle;
 

	
 
		// in case the user doesn't exist
 
		if(high-low <= 0)
 
			return 0;
 
	}
 
}
 

	
 
int deleteUser(struct user *user_ptr)
 
{
 
	free(user_ptr->username);
 
	memset(user_ptr, '\0', sizeof(struct user));
 

	
 
	user_mgr_info.current_users--;
 

	
 
	return 1;
 

	
 
	backup_list_XML();
 
}
 

	
 
int createUser(struct user *user_ptr, char *nameOfUser)
 
{
 
	// clear old memory
 
	memset(user_ptr, '\0', sizeof(struct user));
 

	
 
	user_ptr->username = nameOfUser;
 
	user_ptr->render_power = 0;
 
	user_ptr->last_job = 0;
 

	
 
	user_mgr_info.current_users++;
 

	
 
	return 1;
 
}
 

	
 
// places the new user at position index in the array, moves other users if it needs to
 
int placeUser(int index, char *nameOfUser)
 
{
 
	int higher;
 
	int lower;
 
	int total_moves;
 

	
 
	// I shift data in the array to create an open the space where the user should be added
 
	// but first I figure out which way is the shortest
 
	if(user_mgr_info.user_array[index].username != 0)
 
	{
 
		higher = index + 1;
 
		while(user_mgr_info.user_array[higher].username != 0)
 
			higher++;
 

	
 
		lower = index - 1;
 
		while(user_mgr_info.user_array[lower].username != 0)
 
			lower--;
 

	
 
		// here the data is shifted to open up a space
 
		if(index - lower < higher - index)
 
		  {
 
		    total_moves = index - lower;
 
		    for(; lower < index; lower++)
 
				memcpy(&user_mgr_info.user_array[lower], &user_mgr_info.user_array[lower + 1], sizeof(struct user));
 
		  }
 
		else
 
		  {
 
		    total_moves = higher - index;
 
		    for(; higher > index; higher--)
 
				memcpy(&user_mgr_info.user_array[higher], &user_mgr_info.user_array[higher - 1], sizeof(struct user));
 
		  }
 
	}
 

	
 
	// add the user to the array
 
	createUser(&user_mgr_info.user_array[index], nameOfUser);
 

	
 
	if(total_moves > 50){
 
	  resize_user_array();
 
	}
 
	return 1;
 
}
 

	
 
int addUser(char *nameOfUser)
 
{
 
	int high;
 
	int low;
 
	int middle;
 
	int result;
 
	high = user_mgr_info.user_array_size - 1;
 
	low = 0;
 

	
 
	for(middle = (low+high)/2; 1 == 1; middle = (low+high)/2)
 
	{
 
		// in case middle lands on a part of the array with no user
 
		while(user_mgr_info.user_array[middle].username == 0)
 
		{
 
			if(result < 0)
 
				middle--;
 
			else
 
				middle++;
 
		}
 

	
 
		// this is where the array is cut in half and the half that the nameOfUser is on is kept
 
		result = strcmp(nameOfUser, user_mgr_info.user_array[middle].username);
 
		if(result == 0)
 
			return 0;
 
		else if(result < 0)
 
			high = middle;
 
		else
 
			low = middle;
 

	
 
		// once there are less than 10 possible places for the user to be placed
 
		// break out of this loop and begin next loop
 
		if(high-low <= 10)
 
			break;
 
	}
 

	
 
	// this function starts at the low index number, and goes up until it finds a
 
	// username that is bigger than it alphabetically, and tells the userPlacer
 
	// that it needs to go 1 before that spot
 
	for(; low <= high; low++)
 
	{
 
		while(user_mgr_info.user_array[low].username == 0)
 
			low++;
 

	
 
		result = strcmp(nameOfUser, user_mgr_info.user_array[low].username) < 0;
 
		if(result < 0)
 
		{
 
			placeUser(low - 1, nameOfUser);
 
			return 1;
 
		}
 
		if(result == 0)
 
		{
 
			fprintf(stderr, "user already exists");
 
			return 0;
 
		}
 
	}
 

	
 
	backup_list_XML();
 
	return 0;
 
}
 

	
 
int initialize_users()
 
{
 
  struct stat buffer;
 
  // cif user_list.xml exists
 
  if(stat("user_list.xml", &buffer) == 0)
 
  {
 
    restart_From_XML_backup();
 
  }
 
  else
 
  {
 
    user_mgr_info.current_users = 0;
 

	
 
    user_mgr_info.user_array_size = 50;
 
    user_mgr_info.user_array = malloc(sizeof(struct user) * 50);
 
  }
 

	
 
  // if XML file is not found create new array of size 50
 

	
 

	
 
  return 1;
 
}
 

	
 
/********************************** XMLness *****************************/
 

	
 
int backup_list_XML()
 
{
 
	xmlTextWriterPtr writer;
 
        char *tmp;
 
	int counter;
 

	
 

	
 
	writer = xmlNewTextWriterFilename("user_list.xml", 0);
 
	xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL);
 

	
 
	// create root element user_list
 
	xmlTextWriterStartElement(writer, (xmlChar*)"user_list");
 

	
 
	_distren_asprintf(&tmp, "%d", user_mgr_info.current_users);
 
	xmlTextWriterWriteAttribute(writer, (xmlChar*)"amount_of_users", (xmlChar*)tmp);
 
	free(tmp);
 

	
 
	for(counter = 0; counter < user_mgr_info.user_array_size; counter++)
 
	{
 
		if(user_mgr_info.user_array[counter].username != 0)
 
		{
 
			xmlTextWriterStartElement(writer, (xmlChar*)"user");
 

	
 
			xmlTextWriterWriteAttribute(writer, (xmlChar*)"name", (xmlChar*)user_mgr_info.user_array[counter].username);
 

	
 
			_distren_asprintf(&tmp, "%d", user_mgr_info.user_array[counter].last_job);
 
			xmlTextWriterWriteAttribute(writer, (xmlChar*)"last_job", (xmlChar*)tmp);
 
			free(tmp);
 

	
 
			_distren_asprintf(&tmp, "%d", user_mgr_info.user_array[counter].render_power);
 
			xmlTextWriterWriteAttribute(writer, (xmlChar*)"render_power", (xmlChar*)tmp);
 
			free(tmp);
 

	
 
			xmlTextWriterEndElement(writer);
 
		}
 
	}
 

	
 
	return 0;
 

	
 
}
 

	
 
int restart_From_XML_backup(){
 
  xmlDocPtr doc;
 
  xmlNodePtr cur;
 
  int counter;
 

	
 
  doc = xmlParseFile("user_list.xml");
 
  cur = xmlDocGetRootElement(doc);
 
  if (xmlStrcmp(cur->name, (xmlChar*)"user_list"))
 
    {
 
      fprintf(stderr, "xml document is wrong type");
 
      xmlFreeDoc(doc);
 
      return 1;
 
    }
 

	
 
  user_mgr_info.current_users = atoi((char*)xmlGetProp(cur, (xmlChar*)"amount_of_users"));
 

	
 
  user_mgr_info.user_array_size = user_mgr_info.current_users * 2;
 
  user_mgr_info.user_array = malloc(sizeof(struct user) * user_mgr_info.user_array_size);
 

	
 
  cur = cur->xmlChildrenNode;
 
  for(counter = 0; cur->next; counter++){
 
    user_mgr_info.user_array[counter*2].username = (char*)xmlGetProp(cur, (xmlChar*)"amount_of_users");
 
    user_mgr_info.user_array[counter*2].last_job = atoi((char*)xmlGetProp(cur, (xmlChar*)"last_job"));
 
    user_mgr_info.user_array[counter*2].render_power = atoi((char*)xmlGetProp(cur, (xmlChar*)"render_power"));
 
    cur = cur->next;
 
  }
 

	
 
  return 0;
 
}
src/server/user_mgr.h
Show inline comments
 
/*
 
  Copyright 2009 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca, Matthew Orlando
 

	
 
  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/>.
 
*/
 

	
 
/*
 
 * Stores, keeps track of, retrieves information concerning,
 
 * and backs up to XML DistRen's userdb system
 
 */
 

	
 
#ifndef _DISTREN_USER_MGR_H
 
#define _DISTREN_USER_MGR_H
 

	
 
struct user
 
{
 
	char *username;
 
	int render_power;
 
	int last_job;
 
};
 

	
 

	
 
int restart_From_XML_backup();
 
int backup_list_XML();
 

	
 
#endif
test/check_asprintf.c
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink
 
  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/asprintf.h"
 

	
 
#include <check.h>
 
#include <string.h>
 

	
 
START_TEST (check_asprintf)
 
{
 
  /* tere _must_ be a better way to test this... */
 
  char *teststring = "abcdefghijklmnopqrstuvwxyz";
 
  char *result;
 
  
 
  _distren_asprintf(&result, "I am te%sting\n", teststring);
 
  
 
  fail_unless(strcmp(result, "I am teabcdefghijklmnopqrstuvwxyzting\n") == 0,
 
	      "simple asprintf check failed");
 
}
 
END_TEST
 

	
 
int main(int argc, char *argv[])
 
{
 
  return 0;
 
  /* see http://check.sourceforge.net/doc/check.html/check_6.html#SEC6 */
 
}
 

	
test/check_execio.c
Show inline comments
 
/*
 
  Copyright 2008 Nathan Phillip Brink, Ethan Zonca
 
  Copyright 2010 Nathan Phillip Brink, Ethan Zonca
 

	
 
  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/execio.h"
 

	
 
#include <check.h>
 

	
 
START_TEST (check_execio)
 
{
 
  struct execio *eio;
 
  char *echoargv[] = 
 
    {
 
      "echo",
 
      "test"
 
    };
 

	
 
  char inbuf[20];
 
  size_t bytesread;
 
  
 
  fail_unless(execio_open(&eio, echoargv[0], echoargv) == 0,
 
	      "execio_open failed");
 
  
 
  fail_unless(execio_read(eio, inbuf, sizeof(inbuf) - 1, &bytesread) == 0,
 
	      "error using execio_read\n");
 
  
 
  fail_unless(execio_close(eio) == 0,
 
	      "error using execio_close\n");
 
}
 
END_TEST
 

	
 
int main(int argc, char *argv[])
 
{
 
  return 0;
 
  /* see http://check.sourceforge.net/doc/check.html/check_6.html#SEC6 */
 
}
 

	
0 comments (0 inline, 0 general)