/*
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 .
*/
#include "common/config.h"
#include "slavefuncs.h"
#include "common/asprintf.h"
#include "common/options.h"
#include
#include
#include
#include
#include
#define DEBUG 1
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 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);
/* If we got a frame */
if(haveWork)
{
fprintf(stderr,"Got 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, 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;
}