Changeset - 71f0379b39de
[Not reviewed]
tip default
0 6 0
Nathan Brink (binki) - 15 years ago 2010-10-09 11:29:35
ohnobinki@ohnopublishing.net
Renice execio's newly spawned processes. Fixes bug 8.
6 files changed with 30 insertions and 12 deletions:
0 comments (0 inline, 0 general)
configure.ac
Show inline comments
 
@@ -15,48 +15,51 @@
 
# 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/], [], [http://ohnopub.net/distren/])
 
AC_CONFIG_HEADERS([src/common/config.h])
 
AC_CONFIG_SRCDIR([src/server/distrend.c])
 
AC_CONFIG_MACRO_DIR([m4])
 

	
 
AC_PROG_CC
 
AC_PROG_LIBTOOL
 

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

	
 
dnl these macros force the refered to types to be available without me
 
dnl writing my own magic :-)
 
AC_TYPE_PID_T
 
AC_TYPE_SIZE_T
 

	
 
AC_TYPE_UINT8_T
 
AC_TYPE_UINT16_T
 
AC_TYPE_UINT32_T
 

	
 
dnl execio has a nice() call but it's not vital to our operation
 
AC_CHECK_FUNCS([nice])
 

	
 
dnl selective compilation
 
dnl For now, this is only left for when the C-based client is
 
dnl reintroducded.
 
AC_ARG_ENABLE([server],
 
	[AS_HELP_STRING([--disable-server],[Don't build the distren server])],
 
	[enable_server=$enableval],
 
	[enable_server=yes])
 
AM_CONDITIONAL([ENABLE_SERVER],
 
	[test "x$enable_server" = "xyes"])
 

	
 
dnl package dependencies:
 

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

	
 
LIBS_save="$LIBS"
 
CFLAGS_save="$CFLAGS"
 
LIBS="$LIBS $DISTLIBS_LIBS"
 
CFLAGS="$CFLAGS $DISTLIBS_CFLAGS"
 
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <list.h>
src/common/execio.c
Show inline comments
 
@@ -11,49 +11,49 @@
 
  DistRen is distributed in the hope that it will be useful,
 
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  GNU Affero General Public License for more details.
 

	
 
  You should have received a copy of the GNU Affero General Public License
 
  along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include "common/config.h"
 

	
 
#include "common/execio.h"
 

	
 
#include <unistd.h>
 
#include <sys/types.h>
 
#ifndef _WIN32
 
#include <sys/wait.h>
 
#endif
 
#include <signal.h>
 
#include <fcntl.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <errno.h>
 

	
 
int execio_open(struct execio **rem, const char *progname, char *const argv[])
 
int execio_open(struct execio **rem, const char *progname, char *const argv[], int nice_incr)
 
{
 
  /* pipe used to write to child */
 
  int pipe_write[2];
 
  /* pipe used to read from child */
 
  int pipe_read[2];
 

	
 
  pid_t child;
 

	
 
  /* for wait(2) if needed */
 
  int childstatus;
 
  
 
  int counter;
 
  int counter2;
 
  int maxfds;
 

	
 
  /* create two pipes to facilitate communication with child */
 
  if(pipe(pipe_write))
 
    return 1;
 
  if(pipe(pipe_read))
 
    {
 
      close(pipe_write[0]);
 
      close(pipe_write[1]);
 
      return 1;
 
    }
 
@@ -78,48 +78,53 @@ int execio_open(struct execio **rem, con
 
      /* setup execio struct */
 
      (*rem) = malloc(sizeof(struct execio));
 
      if(!(*rem))
 
	{
 
	  /* we should tell the child we're dead - use wait and close our end of the pipes! */
 
	  close(pipe_write[1]);
 
	  close(pipe_read[0]);
 
	  /* we should probably pass of the wait() call to a thread that just does boring things like that. Especially for when the server tries to connect to other servers... */
 
	  /* maybe we should just kill instead of term the child */
 
	  kill(child, SIGTERM);
 
	  /* the waitpid(2) seems to indicate that only when the child is terminated will this wait return. */
 
	  waitpid(child, &childstatus, 0); 
 
	}
 
      (*rem)->pipe_write = pipe_write[1];
 
      (*rem)->pipe_read = pipe_read[0];
 
      (*rem)->state = 0;
 
      (*rem)->child = child;
 
      
 
      return 0;
 
    }
 
  
 
  /* child */
 
  else
 
    {
 
#ifdef HAVE_NICE
 
      /* lower the nice value */
 
      nice(nice_incr);
 
#endif
 

	
 
      /* close unused pipes */
 
      close(pipe_write[1]);
 
      close(pipe_read[0]);
 

	
 
      /*
 
	reset stdin, stdout, and stderr to the appropriate files. OK, not stderr :-) 
 
      */
 
      dup2(pipe_read[1], STDOUT_FILENO);
 
      dup2(pipe_write[0], STDIN_FILENO);
 
      /*
 
	close the fds that were dup'd
 
       */
 
      close(pipe_read[1]);
 
      close(pipe_write[0]);
 

	
 
      /* 
 
	 close all other file descriptors. We want to keep 0, 1, and 2 open. We don't know that the last open() or pipe() always gives the highest fd number. However, I will assume that it does. Maybe this is a bad idea:
 
       */
 
      counter = pipe_write[0];
 
      if(counter < pipe_write[1])
 
	counter = pipe_write[1];
 
      if(counter < pipe_read[0])
 
	counter = pipe_read[0];
 
      if(counter < pipe_read[1])
 
@@ -141,49 +146,49 @@ int execio_open(struct execio **rem, con
 
      fprintf(stderr, "uh-oh, ``%s'' didn't start for execio\n", progname);
 
      exit(1);
 
    }
 
}
 

	
 
/*
 
  returns 1 if child has exited, 
 
  returns 0 if child is still alive
 
 */
 
int _execio_checkpid(struct execio *eio)
 
{
 
  int childstatus;
 
  
 
#ifdef _WIN32
 
  waitpid(eio->child, &childstatus, 0);
 
#else
 
  waitpid(eio->child, &childstatus, WNOHANG);
 
#endif
 
  /* perror()? */
 

	
 
  return WIFEXITED(childstatus);
 
}
 

	
 

	
 
int execio_read(struct execio *eio, const void *buf, size_t len, size_t *bytesread)
 
int execio_read(struct execio *eio, void *buf, size_t len, size_t *bytesread)
 
{
 
  ssize_t ret;
 
  /*
 
    TODO: detect NULL eio? 
 
    TODO: errno?
 
    update status of eio for execio_status/to be able to cleanup subproc??
 

	
 
    whenever read() returns 0, it means EOF
 
   */
 
  
 
  ret = read(eio->pipe_read, buf, len);
 
  if(ret == -1)
 
    {
 
      (*bytesread) = 0;
 
      perror("read");
 
      switch(errno)
 
	{
 
	case EAGAIN:
 
	case EINTR:
 
	  return 0;
 
	  break;
 
	default:
 
	  return 1;
 
	}
src/common/execio.h
Show inline comments
 
@@ -23,52 +23,62 @@
 
/*
 
  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
 
 * \brief
 
 *   runs progname with the arguments in argv. argv must be null terminated!!!!!!!!!
 
 *
 
 * \param eio
 
 *   Where to store the pointer to the execio handle.
 
 * \param progname
 
 *   The name of the program to execute at the system's shell.
 
 * \param argv
 
 *   The NULL-terminated list of arguments to send to the subcommand.
 
 * \param nice_incr
 
 *   The amount to increase the subprogram's nice value by. See nice(3p).
 
 * \return
 
 *   nonzero return on error
 
*/
 
int execio_open(struct execio **eio, const char *progname, char *const argv[]);
 
int execio_open(struct execio **eio, const char *progname, char *const argv[], int nice_incr);
 

	
 
/**
 
   doesn't block,
 
   returns 0 on success, 1 on failure
 
*/
 
int execio_read(struct execio *eio, const void *buf, size_t len, size_t *bytesread);
 
int execio_read(struct execio *eio, void *buf, size_t len, size_t *bytesread);
 
int execio_write(struct execio *eio, const 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/remoteio.c
Show inline comments
 
@@ -574,49 +574,49 @@ size_t remoteio_sendq_len(const struct r
 
  return (size_t)q_size(rem->outmsgs);
 
}
 

	
 
/**
 
   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);
 
  rtn = execio_open( &rem->execio, "ssh", sshargs, 0);
 
  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, const 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;
src/server/slavefuncs.c
Show inline comments
 
@@ -278,49 +278,49 @@ int exec_blender(char *input, char *outp
 
  stringToUpper(outputExt);
 

	
 
  char *frame_str;
 
  _distren_asprintf(&frame_str, "%i", frame);
 

	
 
  char *xres_str;
 
  _distren_asprintf(&xres_str, "%i", xres);
 

	
 
  char *yres_str;
 
  _distren_asprintf(&yres_str, "%i", yres);
 

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

	
 
  if(DEBUG)
 
    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;
 

	
 
  if(DEBUG)
 
    fprintf(stderr,"Executing: %s\n", frame_str);
 

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

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

	
 
void xmlcleanup()
 
@@ -706,49 +706,49 @@ int _web_getwork(int slavekey, char *sla
 
      if(tmp == NULL)
 
        return 0; // no work
 
      *framenum = atoi(tmp); // Grab framenum
 

	
 
      tmp = strtok (NULL, ",");
 
      if(tmp == NULL)
 
        return 0; // no work
 
      serverversion = tmp;
 

	
 
      tmp = strtok (NULL, ",");
 
      if(tmp == NULL)
 
        return 0; // no work
 
      *xres = atoi(tmp);
 

	
 
      tmp = strtok (NULL, ",");
 
      if(tmp == NULL)
 
        return 0; // no work
 
      *yres = atoi(tmp);
 

	
 
      tmp = strtok (NULL, ",");
 
      if(tmp == NULL)
 
        return 0; // no work
 
      *outputext = strdup(tmp);
 
      if(DEBUG)
 
        fprintf(stderr,"GETWORK Debug output - Job: %d | Frame: %d | Xres: %d | Yres: %d | Outformat: %s\n", *jobnum, *framenum, *xres, *yres, outputext);
 
        fprintf(stderr,"GETWORK Debug output - Job: %d | Frame: %d | Xres: %d | Yres: %d | Outformat: %s\n", *jobnum, *framenum, *xres, *yres, *outputext);
 

	
 
      // @FIXME: Setting outputext and serverversion = temp; will this cause issues as these are pointers to parts of the original temp var?
 

	
 
      // @TODO: This should be called every time, not just on fail.
 
      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;
 
      }
 
      if(DEBUG)
 
        fprintf(stderr,"Software versions: %s local vs %s remote\n", PACKAGE_VERSION, serverversion);
 

	
 

	
 
      free(data.memory);
 
      return 1;
 
    }
 
    else
 
      return 0; // error
 
  }
 
}
 

	
 
void _web_setrenderpower(int slavekey, char *slavepass, int renderpower){
 
  fprintf(stderr,"Setting render power on server... ");
 
  char *url;
 
  _distren_asprintf(&url,"http://distren.org/slave/act.php?mode=setrenderpower&slavekey=%d&slavepass=%s&renderpower=%d", slavekey, slavepass, renderpower);
 
@@ -774,49 +774,49 @@ int slaveBenchmark(char *datadir, int *b
 
  _distren_asprintf(&realOutput, "%s/benchmark%d.jpg", datadir, frameToRender); // Where to save benchmark output
 

	
 

	
 
  char *input;
 
  _distren_asprintf(&input, "%s/benchmark.blend", datadir); // Input file
 

	
 
  fprintf(stderr,"Downloading benchmark data...\n");
 
  curlget("http://data.distren.org/benchmark.blend", input); // Download to input file location
 

	
 
  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);
 
  fprintf(stderr,"Running benchmark...\n");
 

	
 
  long startTime;
 
  long endTime;
 

	
 
  time(&startTime);
 

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

	
 
  ret = execio_open(&testrem, command, cmd);
 
  ret = execio_open(&testrem, command, cmd, 10);
 
  if(ret)
 
    {
 
      fprintf(stderr, "Error executing 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);
 

	
 
  time(&endTime);
 

	
 
  struct stat buffer;
 
  int ostatus = stat(realOutput, &buffer);
 
  if(ostatus == -1){
 
    ret = 1; // Return error if output wasn't generated
 
  }
 
  else
 
    remove(output);
 

	
test/check_execio.c
Show inline comments
 
@@ -18,49 +18,49 @@
 
 */
 

	
 
#include "check_execio.h"
 

	
 
#include "common/execio.h"
 

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

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

	
 
  char inbuf[20];
 
  size_t bytesread;
 
  size_t pos;
 

	
 
  pos = 1;
 

	
 
  fail_unless(execio_open(&eio, echoargv[0], echoargv) == 0,
 
  fail_unless(execio_open(&eio, echoargv[0], echoargv, 0) == 0,
 
	      "execio_open failed");
 
  
 
  fail_unless(execio_read(eio, inbuf, sizeof(inbuf) - 1, &bytesread) == 0,
 
	      "error using execio_read\n");
 
  pos += bytesread;
 

	
 
  while (bytesread && pos < (sizeof(inbuf) - 1))
 
    {
 
      execio_read(eio, &inbuf[pos], sizeof(inbuf) - 1 - pos, &bytesread);
 
      pos += bytesread;
 
    }
 

	
 
  fail_if (pos > 10,
 
	   "I read more than 10 bytes from the command ``echo test'' when I expected only 5 or 6 bytes");
 

	
 
  inbuf[sizeof(inbuf) - 1] = '\0';
 
  fail_if (strncmp(inbuf, "test", 4),
 
	   "Expecting ``test''; got ``%s''",
 
	   inbuf);
 

	
 
  fail_unless(execio_close(eio) == 0,
 
	      "error using execio_close\n");
 
}
 
END_TEST
0 comments (0 inline, 0 general)