Files @ e129dd8d5b5c
Branch filter:

Location: DistRen/src/server/distrenjob.c

ethanzonca
Added debug define output
/*
  Copyright 2009 Nathan Phillip Brink <ohnobinki@ohnopublishing.net>

  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 "distrenjob.h"
#include "slavefuncs.h"

#include "common/asprintf.h"

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlwriter.h>

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); /* </watchdog> */

  xmlTextWriterEndElement(writer); /* </job> */

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