/* Copyright 2009 Nathan Phillip Brink 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 "distrenjob.h" #include "slavefuncs.h" #include "common/asprintf.h" #include #include #include void distrenjob_free(struct distrenjob **distrenjob) { struct distrenjob *dj; dj = *distrenjob; xmlFree(dj->name); xmlFree(dj->submitter); free(dj->frameset); free(dj); *distrenjob = NULL; } int distrenjob_new(struct distrenjob **distrenjob) { struct distrenjob *dj; dj = malloc(sizeof(struct distrenjob)); if(!dj) { /* try to catch code that doesn't respect return values faster: */ *distrenjob = NULL; return 1; } *distrenjob = dj; dj->next = NULL; dj->jobnum = 0; /*< @todo there should be a central jobnum allocator and a way to save the maximum jobnumber allocated */ dj->type = 1; dj->name = (char *)NULL; dj->submitter = (char *)NULL; dj->output_format = (char *)NULL; dj->width = 0; dj->height = 0; dj->priority = 0; dj->completed_frames = 0; dj->assigned_frames = 0; dj->total_frames = 0; dj->prev_frame_index = -1; dj->total_render_time = 0; dj->assigned_render_power = 0; dj->watchdog_forgiveness = 3600; // initialize watchdog forgiveness at 1 hour dj->hibernate = 0; dj->frameset = (struct frameset *)NULL; /*< @todo does frameset need to be initialized here? */ return 0; } /** read an unsigned integer property from an xmlNode convenience function for distrenjob_unserialize() @param xmlnode may be NULL for convenience @return 0 on success, other on error */ int _distrenjob_xml_readuint(xmlNodePtr xmlnode, xmlChar *propname, unsigned int *num) { xmlChar *string; if(!xmlnode) return 1; string = xmlGetProp(xmlnode, propname); if(!string) { fprintf(stderr, "_distrenjob_xml_readuint(): warning: unable to get property ``%s''\n", propname); return 1; } *num = (unsigned int)strtoul((char *)string, (char **)NULL, 10); xmlFree(string); return 0; } /** Loads a distrenjob that was serialized with distrenjob_serialize() @see distrenjob_serialize() @param pathtoxml path to XML file @param distrenjob will be set to a pointer */ int distrenjob_unserialize(struct distrenjob **distrenjob, char *pathtoxml) { struct distrenjob *dj; struct frameset *fs; unsigned int start_frame; unsigned int end_frame; unsigned int counter; struct tm tm; xmlDocPtr xmldoc; xmlNodePtr xmlnode; xmlChar *xmlchar; xmlXPathContextPtr xmlxpathcontext; int tmp; if(distrenjob_new(distrenjob)) return 1; dj = *distrenjob; xmldoc = xmlReadFile(pathtoxml, NULL, XML_PARSE_PEDANTIC); if(!xmldoc) { /** @todo are we able to depend on libxml2's printed errors or channel them into syslog output (eventually)? Currently, this error is repetitious of a libxml2 error printed on stderr for us. */ fprintf(stderr, "error reading XML file ``%s''\n", pathtoxml); distrenjob_free(distrenjob); return 2; } xmlxpathcontext = xmlXPathNewContext(xmldoc); xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job"); if(!xmlnode) { distrenjob_free(distrenjob); return 3; } /*< @todo should we use xmlChar everywhere too? */ dj->name = (char *)xmlGetProp(xmlnode, (xmlChar *)"name"); if(!dj->name) { distrenjob_free(distrenjob); return 4; } /*< @todo validation needs to be done on usernames. e.g., currently, they shouldn't contain the '"' char */ dj->submitter = (char *)xmlGetProp(xmlnode, (xmlChar *)"submitter"); if(!dj->submitter) { distrenjob_free(distrenjob); return 5; } tmp = _distrenjob_xml_readuint(xmlnode, (xmlChar *)"priority", &dj->priority); if(tmp) { distrenjob_free(distrenjob); return 6; } dj->output_format = (char *)xmlGetProp(xmlnode, (xmlChar *)"output_format"); if(!dj->output_format) { distrenjob_free(distrenjob); return 6; } tmp = _distrenjob_xml_readuint(xmlnode, (xmlChar *)"jobnum", &dj->jobnum); if(tmp) { distrenjob_free(distrenjob); return 7; } xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/resolution"); tmp = _distrenjob_xml_readuint(xmlnode, (xmlChar *)"width" , &dj->width ); tmp += _distrenjob_xml_readuint(xmlnode, (xmlChar *)"height" , &dj->height ); xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/video"); tmp += _distrenjob_xml_readuint(xmlnode, (xmlChar *)"start_frame", &start_frame); tmp += _distrenjob_xml_readuint(xmlnode, (xmlChar *)"end_frame", &end_frame); if(tmp) { fprintf(stderr, "distrenjob_unserialize(): error reading integer values from ``%s''\n", pathtoxml); distrenjob_free(distrenjob); return 8; } /** total_render_time and watchdog stuff doesn't need error checking, just good defaults */ xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/stats"); dj->total_render_time = 0; xmlchar = xmlGetProp(xmlnode, (xmlChar *)"total_render_time"); if(xmlchar) { if(strptime((char *)xmlchar, "%D %T", &tm)) dj->total_render_time = mktime(&tm); xmlFree(xmlchar); } xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/job/watchdog"); dj->watchdog_forgiveness = 3600; tmp =_distrenjob_xml_readuint(xmlnode, (xmlChar *)"forgiveness", &dj->watchdog_forgiveness); if(tmp) fprintf(stderr, "distrenjob_unserialize(): warning: watchdog forgiveness is unspecified in ``%s'', defaulting to 3600\n", pathtoxml); xmlXPathFreeContext(xmlxpathcontext); xmlFreeDoc(xmldoc); /** reconstruct the frameset */ dj->total_frames = end_frame - start_frame + 1; #ifndef NDEBUG fprintf(stderr, "distrenjob_unserialize(): restoring %d frames\n", dj->total_frames); #endif dj->frameset = malloc(sizeof(struct frameset) * dj->total_frames); if(!dj->frameset) { fprintf(stderr, "OOM!\n"); distrenjob_free(distrenjob); return 9; } fs = dj->frameset; for(counter = start_frame; counter <= end_frame; counter ++) { fs->num = counter; fs->status = FRAMESETSTATUS_UNASSIGNED; /*< @todo job partial completion and resumption support */ fs ++; } #ifndef NDEBUG fprintf(stderr, "distrenjob_unserialize(): finished loading ``%s''\n", pathtoxml); #endif return 0; } int distrenjob_serialize(struct distrenjob *job, char *outfile) { xmlTextWriterPtr writer; char *tmp; char *tmpfile; int tmprtn; /** transactional FS access for POSIX systems... (probably not implemented correctly) */ _distren_asprintf(&tmpfile, "%s_", outfile); /* create xml document at the location tmp with no compression */ writer = xmlNewTextWriterFilename(tmpfile, 0); xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL); /** write distrenjob element and add its attributes */ xmlTextWriterStartElement(writer, (xmlChar*)"job"); xmlTextWriterWriteAttribute(writer, (xmlChar*)"name", (xmlChar*)job->name); xmlTextWriterWriteAttribute(writer, (xmlChar*)"submitter", (xmlChar*)job->submitter); _distren_asprintf(&tmp, "%d", job->priority); xmlTextWriterWriteAttribute(writer, (xmlChar*)"priority", (xmlChar*)tmp); free(tmp); _distren_asprintf(&tmp, "%d", job->jobnum); xmlTextWriterWriteAttribute(writer, (xmlChar *)"jobnum", (xmlChar *)tmp); free(tmp); xmlTextWriterWriteAttribute(writer, (xmlChar *)"output_format", (xmlChar *)job->output_format); /** write resolution element and add its attributes */ xmlTextWriterStartElement(writer, (xmlChar*)"resolution"); _distren_asprintf(&tmp, "%d", job->width); xmlTextWriterWriteAttribute(writer, (xmlChar*)"width", (xmlChar*)tmp); free(tmp); _distren_asprintf(&tmp, "%d", job->height); xmlTextWriterWriteAttribute(writer, (xmlChar*)"height", (xmlChar*)tmp); free(tmp); xmlTextWriterEndElement(writer); /** write video element and its attributes */ xmlTextWriterStartElement(writer, (xmlChar*)"video"); _distren_asprintf(&tmp, "%d", job->frameset[0].num); xmlTextWriterWriteAttribute(writer, (xmlChar*)"start_frame", (xmlChar*)tmp); free(tmp); _distren_asprintf(&tmp, "%d", job->frameset[ job->total_frames - 1 ].num); xmlTextWriterWriteAttribute(writer, (xmlChar*)"end_frame", (xmlChar*)tmp); free(tmp); xmlTextWriterWriteAttribute(writer, (xmlChar*)"output_format", (xmlChar*)job->output_format); xmlTextWriterEndElement(writer); /** write watchdog forgiveness element */ xmlTextWriterStartElement(writer, (xmlChar*)"watchdog"); _distren_asprintf(&tmp, "%d", job->watchdog_forgiveness); xmlTextWriterWriteAttribute(writer, (xmlChar *)"forgiveness", (xmlChar *)tmp); free(tmp); xmlTextWriterEndElement(writer); /* */ xmlTextWriterEndElement(writer); /* */ /** end document */ xmlTextWriterEndDocument(writer); /** free writer and save xml file to disk */ xmlFreeTextWriter(writer); /** This is the key to transactioanl POSIX FS programming. */ tmprtn = rename(tmpfile, outfile); if(tmprtn == -1) { fprintf(stderr, "%s:%d: Error renaming ``%s'' to ``%s''\n", __FILE__, __LINE__, tmpfile, outfile); perror("rename"); tmprtn = 1; } free(tmpfile); return tmprtn; }