/* 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 "slavefuncs.h" #include "common/asprintf.h" #include "common/options.h" #include "common/protocol.h" #include "common/remoteio.h" #include #include #include #include #include #define DEBUG 0 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; // struct distrenjob *myjob; /* Structure to hold data gathered from the XML file - not needed anymore? */ struct remoteio *comm_slave; /** initializations */ datadir = NULL; server = NULL; username = NULL; password = NULL; hostname = NULL; char curopt; int runBenchmark = 0; while(((char)-1) != (curopt = getopt(argc, argv, "u: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: distrenslave [option] \nStarts a distren slave\n\t-u\tset username (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); if(DEBUG) 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 distrenslave.conf!\n"); 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 *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; /* No longer used, url to .blend on server */ char *pathtoJob; /* Full 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 Slave Pre-Alpha %s\n- Experimental build: Use at your own risk!\n\n", PACKAGE_VERSION); int benchmarkTime = 0; int renderPower = 0; // @TODO: add call to function to force recalc if $render_power == "" 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 fprintf(stderr,"Requesting work...\n"); haveWork = getwork(comm_slave, &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); int dlret = downloadTar(urltoTar, pathtoTar); if(dlret == 0) fprintf(stderr,"Data retrieved successfully!\n"); else if(dlret == 3){ resetframe(comm_slave, 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 tarball %s...\n", pathtoTar); // Decompress tarball struct stat jbuffer; int jstatus = stat(pathtoTar, &jbuffer); if(jstatus == -1){ if(DEBUG) fprintf(stderr,"Main job file does not exist, extracting...\n"); // If error unpacking tarball if(unpackJob(pathtoJob, pathtoTar)){ resetframe(comm_slave, jobnum, framenum); // Unassign the frame on the server so other slaves can render it fprintf(stderr,"Error decompressing tarball! Exiting.\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); } /* Execute blender */ if(exec_blender(pathtoJobfile, pathtoOutput, framenum)) { fprintf(stderr,"Error running Blender. Check your installation and/or your PATH.\n"); resetframe(comm_slave, 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); resetframe(comm_slave, 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 frame.\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 of "jobjum.framenum" finishframe(comm_slave, jobnum, framenum); } } else{ if(DEBUG) fprintf(stderr,"Nothing 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); } sleep(5); // Poll 5 seconds. @TODO: Remove all polling } fprintf(stderr,"Closing connection to server...\n"); remoteio_close(comm_slave); 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; }