/* 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 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; }