Files @ d231345767d5
Branch filter:

Location: DistRen/src/server/simpleslave.c

ethanzonca
Added output extension and resolution support in render calls and _web calls, breaks slave.c
/*
  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 "common/config.h"

#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 0


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

  multiio_context_t multiio;

  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;
  int runBenchmark = 0;
  int updateConf = 0;
  multiio = multiio_context_new();

  while(((char)-1) != (curopt = getopt(argc, argv, "u:p:rh")))
     {
       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: distrensimpleslave [option] \nStarts a distren slave\n\t-u\tset Slave ID/Username (run after fresh install)\n\t-p\tset Slave Passphrase (run after fresh install)\n\t-r\tRecalculate render power (benchmark)\n\t-h\tshow this help\n");
           return 2;
         }
       else if(curopt == 'r')
         {
           runBenchmark = 1;
           break;
         }
       else if(curopt == 'u'){
         username = strdup(optarg);
         updateConf = 1;
       }
       else if(curopt == 'p'){
         password = strdup(optarg);
         updateConf = 1;
       }
     }

if(updateConf){
  if(DEBUG)
    fprintf(stderr, "Putting Slave ID \"%s\" and Slave Passphrase \"%s\" in distrenslave.conf\n", username, password);
  if(username != NULL)
    conf_replace("distrenslave.conf", "!username", username);
  if(password != NULL)
    conf_replace("distrenslave.conf", "!password", password);

  fprintf(stderr, "Please invoke distrensimpleslave with no arguments to run with the slave ID and/or passphrase you just set\n");
  return 0;
}
  /* Get conf data */
 options_init(argc, argv, &my_cfg, myopts, "slave", &commonopts, multiio);

  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, try the -u flag \n");
      return 1;
    }
  if(!password)
    {
      fprintf(stderr, "password not set, try the -p flag\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 distrenslave.conf or try the -p flag!\n");
      return 1;
    }

  // Variables needed for main loop
  int jobnum = 0;
  int framenum = 0;
  int xres = 0;
  int yres = 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 uncompressed 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 *urltoJobfile; /* Used if we aren't using compression */
  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,"\nDistRen SimpleSlave Pre-Alpha %s\n- Experimental build: Use at your own risk!\n\n", PACKAGE_VERSION);

  /* @TODO @FIXME create datadir (/var/distren/) if it doesn't exist, or notify the user */

  int benchmarkTime = 0;
  int renderPower = 0;

  // @TODO: create _web function to force recalc if !isset($render_power) on server
  if(runBenchmark)
    {
      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);
	  return 0;
	}
    }

  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, &xres, &yres, outputExt);

    /* If we got a frame */
    if(haveWork)
      {
        fprintf(stderr,"\nGot work from server...\n");
        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);

        // Check if tar exists already
        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
            fprintf(stderr,"Extraction failed, exiting.\n");
            return 1;
          }
	  else
	    if(DEBUG)
              fprintf(stderr,"Whatever happened in the unpackjob() function supposedly worked.\n");
        }
	else{
	  if(DEBUG)
	    fprintf(stderr,"Using already extracted data.\n");
        }

        struct stat tbuffer;
        int tstatus = stat(pathtoJobfile, &tbuffer);
	if(tstatus == -1){
          fprintf(stderr,"The main job file does not exist! Data may be corrupt, or the archive is malformatted.\n");
          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, outputExt, xres, yres, 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(60); // 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;
}