Changeset - 038a38cef284
[Not reviewed]
default
0 2 0
Nathan Brink (binki) - 16 years ago 2009-11-29 23:22:14
ohnobinki@ohnopublishing.net
listen block eating, config parse error handling
2 files changed with 22 insertions and 3 deletions:
0 comments (0 inline, 0 general)
src/common/options.c
Show inline comments
 
@@ -80,161 +80,163 @@ int options_init(int argc, char *argv[],
 
     && (configfileprefix = getenv("DISTREN_CONFIG_PREFIX")) )
 
    {
 
      configfileprefix = strdup(configfileprefix);
 
      if(!configfileprefix)
 
	{
 
	  fprintf(stderr, "OOM\n");
 
	  return 1;
 
	}
 
    }
 
  /* For those of you who don't know, the following is an example of concatenation of constant strings by the C compiler. Now, that doesn't mean you can do run-time string concatenation ;-)
 
     strdup is because someday configfile will be customizable via argv[]
 
  */
 
  if(!configfileprefix)
 
      configfileprefix = strdup(SYSCONFDIR "/distren");
 
  if(!configfileprefix)
 
    {
 
      fprintf(stderr, "OOM\n");
 
      return 1;
 
    }
 
  
 
  i = strlen(configfileprefix) + strlen(myname) + strlen(".conf") + 1;
 
  configfile = malloc(i);
 
  if(!configfile)
 
    {
 
      perror("malloc");
 
      return 1;
 
    }
 
  strncpy(configfile, configfileprefix, strlen(configfileprefix) + 1);
 
  strcat(configfile, myname);
 
  strcat(configfile, ".conf");
 
  fprintf(stderr, "using configuration file: %s\n", configfile);
 

	
 

	
 
  /* initialize structs */
 
  *allopts = malloc(sizeof(struct options_common));
 
  if(!*allopts)
 
    {
 
      perror("malloc");
 
      free(configfile);
 
      return 1;
 
    }
 
  memset(*allopts, '\0', sizeof(struct options_common));
 

	
 
  (*allopts)->data = malloc(sizeof(struct options_common_data));
 
  if(!(*allopts)->data)
 
    {
 
      perror("malloc");
 
      free(configfile);
 
      free(*allopts);
 
      return 1;
 
    }
 
  memset((*allopts)->data, '\0', sizeof(struct options_common_data));
 

	
 
  (*allopts)->remoteio = malloc(sizeof(struct remoteio_opts));
 
  if(!(*allopts)->data)
 
    {
 
      perror("malloc");
 
      free(configfile);
 
      free((*allopts)->data);
 
      free(*allopts);
 
      return 1;
 
    }
 
  memset((*allopts)->remoteio, '\0', sizeof(struct remoteio_opts));
 
  (*allopts)->remoteio->ssh_command = strdup("ssh");
 
  /** @TODO: check for NULL return above */
 

	
 

	
 
  cfg_opt_t common_opts[] =
 
    {
 
      CFG_SIMPLE_STR("ssh-command", &(*allopts)->remoteio->ssh_command),
 
      CFG_END()
 
    };
 

	
 
  /**
 
    In these arrays, I should replace NULL with a pointer to a string in the struct which should hold the result of confuse processing an option. This is partially because confuse will not free the values it parses.
 
    the server sections in the distrencommon.conf describe different servers that the client may connect to
 
   */
 
  cfg_opt_t server_opts[] =
 
    {
 
      CFG_STR("username", NULL, CFGF_NONE),
 
      CFG_STR("hostname", NULL, CFGF_NONE),
 
      CFG_STR("method", "ssh", CFGF_NONE),
 
      CFG_STR_LIST("types", NULL, CFGF_NONE),
 
      CFG_END()
 
    };
 

	
 
  cfg_opt_t opts[] =
 
    {
 
      CFG_SEC("common",
 
	      common_opts,
 
	      CFGF_NONE),
 
      CFG_SEC("server",
 
	      server_opts,
 
	      CFGF_MULTI | CFGF_TITLE),
 
      CFG_SEC(myname,
 
	      myopts,
 
	      0),
 
	      CFGF_NONE),
 
      CFG_FUNC("include",
 
	       &cfg_include),
 
      CFG_END()
 
    };
 

	
 

	
 

	
 
  (*allopts)->data->cfg = cfg_init(opts, 0);
 
  switch(cfg_parse((*allopts)->data->cfg, configfile))
 
    {
 
    default:
 
      fprintf(stderr, "cfg_parse returned an unknown error code\n");
 
    case CFG_FILE_ERROR:
 
      cfg_error((*allopts)->data->cfg, "Couldn't open file ``%s''\n", configfile);
 
      /* no break; on purpose */
 

	
 
    case CFG_PARSE_ERROR:
 

	
 
      fprintf(stderr, "error parsing configuration file, exiting\n");
 

	
 
      free((*allopts)->data);
 
      free(*allopts);
 
      free(configfile);
 

	
 
      return 1;
 

	
 
    case CFG_SUCCESS:
 
      break;
 
    }
 

	
 
  free(configfile);
 

	
 
  *mycfg = cfg_getsec((*allopts)->data->cfg, myname);
 

	
 

	
 
  /*
 
   * libdistrencommon's config options:
 
   */
 

	
 
  /* remoteio -- iterate through servers */
 
  i = remoteio_config((*allopts)->data->cfg, (*allopts)->remoteio);
 
  if(i)
 
    {
 
      fprintf(stderr, "cleanup\n");
 
      abort();
 
      return 1;
 
    };
 

	
 
  return 0;
 
}
 

	
 
/**
 
   @todo free remoteio_servers
 
*/
 
int options_free(struct options_common *opts)
 
{
 
  /* free the other common_options struct's members */
 

	
 
  cfg_free(opts->data->cfg);
 

	
 
  free(opts->remoteio);
 
  free(opts->data);
 
  free(opts);
 

	
 
  return 0;
 
}
src/server/distrend.c
Show inline comments
 
@@ -515,227 +515,243 @@ int find_jobframe_again(struct general_i
 
  if(!distrenjob_ptr || distrenjob_ptr->prev_frame_index >= (distrenjob_ptr->total_frames - 1))
 
    {
 
      fprintf(stderr, "Job number %d has been finished, finding new job\n", jobnum);
 

	
 
      // if previous job isn't yet finished the render power of the slave is removed from it
 
      if(distrenjob_ptr)
 
    	  distrenjob_ptr->assigned_render_power = distrenjob_ptr->assigned_render_power - rend_pwr;
 

	
 
      return find_jobframe_new(geninfo, rend_pwr, job, frame);
 
    }
 

	
 
  found = 0;
 
  found = find_jobframe_from_job(distrenjob_ptr, job, frame);
 

	
 
  if(found)
 
	  find_jobframe_new(geninfo, rend_pwr, job, frame);
 

	
 
  return 0;
 
}
 

	
 
/** Checks for dead, latent, or stale slaves */
 
void frame_watchdog(struct distrenjob *distrenjob_head)
 
{
 
  struct distrenjob *distrenjob_ptr;
 
  unsigned int counter;
 

	
 
  for(distrenjob_ptr = distrenjob_head->next; distrenjob_ptr; distrenjob_ptr = distrenjob_ptr->next)
 
  /* iterate through jobs */
 
  {
 
    /* if the job has been started, checks by seeing if either to first or second frame has been started */
 
    if(distrenjob_ptr->frameset[0].status != FRAMESETSTATUS_UNASSIGNED || distrenjob_ptr->frameset[1].status != FRAMESETSTATUS_UNASSIGNED)
 
      /* iterate through all frames for this job: */
 
      for(counter = 0; counter < distrenjob_ptr->total_frames; counter ++)
 
	    /*watchdog_forgiveness = seconds of forgiveness before frame is re-assigned: */
 
        if((distrenjob_ptr->frameset[counter].start_time + distrenjob_ptr->watchdog_forgiveness) < clock())
 
        {
 
            /*
 
            If frame is not completed within the number of seconds specified by watchdog_forgiveness
 
            Then change the frame status to unassigned
 
            */
 
            distrenjob_ptr->frameset[counter].status = FRAMESETSTATUS_UNASSIGNED;
 
            distrenjob_ptr->assigned_frames--;
 
        }
 

	
 
    updateJobStatsXML(distrenjob_ptr);
 
  }
 
}
 

	
 
/**
 
   Finds a distrenjob struct based on the jobnum
 
   @arg jobnum job number to search for
 
   @return NULL on job doesn't exist
 
 */
 
struct distrenjob *distrenjob_get(struct distrenjob *head, jobnum_t jobnum)
 
{
 
  struct distrenjob *distrenjob_ptr;
 

	
 
  /*
 
    The conditions of the for loop will leave distrenjob_ptr at NULL if the end of the list is reached. It will leave it pointing to the correct job if it is found.
 
   */
 
  for(distrenjob_ptr = head;
 
      distrenjob_ptr
 
        && distrenjob_ptr->jobnum != jobnum;
 
      distrenjob_ptr = distrenjob_ptr->next);
 

	
 
  return distrenjob_ptr;
 
}
 

	
 

	
 
/**
 
   Removes a distrenjob from the distrenjob linked list. It does not free the distrenjob, however. You should do that with distrenjob_free() from distrenjob.h
 

	
 
   @arg head pointer to the head of the linkedlist of distrenjobs
 
 */
 
void distrenjob_remove(struct general_info *geninfo, struct distrenjob *bj)
 
{
 
  struct distrenjob *previous_distrenjob;
 

	
 
  for(previous_distrenjob = &geninfo->head;
 
      previous_distrenjob
 
	&& previous_distrenjob->next != bj; /*< stop on the distrenjob that comes before bj */
 
      previous_distrenjob = previous_distrenjob->next)
 
    ;
 

	
 
  /*
 
    This removes references to bj from the linked list. I.E., we now skip bj when iterating through the list
 
  */
 
  previous_distrenjob->next = bj->next;
 

	
 
  geninfo->jobs_in_queue --;
 
}
 

	
 

	
 
/* Grabs config info from confs */
 
int distrend_do_config(int argc, char *argv[], struct distrend_config **config)
 
{
 
  unsigned int counter;
 

	
 
  cfg_opt_t myopts_listen[] =
 
    {
 
      CFG_SIMPLE_STR("type", NULL),
 
      CFG_SIMPLE_STR("path", NULL),
 
      CFG_SIMPLE_INT("port", NULL),
 
      CFG_END()
 
    };
 
  cfg_opt_t myopts[] =
 
    {
 
      CFG_SEC("listen",  /* this must be imported into struct listens (which must still be declared) */
 
          myopts_listen,
 
          CFGF_MULTI),
 
      CFG_SIMPLE_STR("datadir", NULL),
 
      CFG_END()
 
    };
 

	
 
  struct distrenjob *distrenjob;
 
  cfg_t *cfg_listen;
 

	
 
  int tmp;
 

	
 
  /*
 
   * test xml2distrenjob()
 
   */
 
  tmp = xml2distrenjob(&distrenjob, "distrenjob.xml.example");
 
  if(tmp)
 
    fprintf(stderr, "xml2distrenjob() returned %d. Try to cd to distren/doc if you want to test out the xml2distrenjob() function. (This will only fix this error if the error is due to an inability of the xml library to access distrenjob.xml.example)\n\n", tmp);
 
  else
 
    fprintf(stderr, "using email ``%s'' for user ``%s'' -- reading in XML files and pulling data from them using libxml2+XPath works!!!\n", distrenjob->email, distrenjob->submitter);
 

	
 
  fprintf(stderr, "%s:%d running config\n", __FILE__, __LINE__);
 

	
 
  *config = malloc(sizeof(struct distrend_config));
 
  myopts[1].simple_value = &(*config)->datadir;
 

	
 
  options_init(argc, argv, &(*config)->mycfg, myopts, "daemon", &(*config)->options);
 
  if(options_init(argc, argv, &(*config)->mycfg, myopts, "daemon", &(*config)->options))
 
    return 1;
 

	
 
  /**
 
     grab listen blocks:
 
   */
 
  (*config)->listens = malloc(sizeof(struct distrend_listen) * (cfg_size((*config)->mycfg, "listen") + 1));
 
  for(counter = 0; counter < cfg_size((*config)->mycfg, "listen"); counter ++)
 
    {
 
      cfg_listen = cfg_getnsec((*config)->mycfg, "listen", counter);
 
      (*config)->listens[counter].port = cfg_getint(cfg_listen, "port");
 
      (*config)->listens[counter].sock = -1;
 
    }
 
  memset(&(*config)->listens[counter], '\0', sizeof(struct distrend_listen));
 

	
 
  fprintf(stderr, "using %s as datadir\n", (*config)->datadir);
 

	
 
  return 0;
 
}
 
int distrend_config_free(struct distrend_config *config)
 
{
 
  options_free(config->options);
 
  free(config);
 

	
 
  return 0;
 
}
 
/* ************************** XML Functions ************************* */
 

	
 
// writes the general_info.xml file which is a copy of the general_info structure
 
// except that it doesn't hold free_clients and rendering_clients
 
void update_general_info(struct general_info *geninfo)
 
{
 
  xmlTextWriterPtr writer;
 
  char *tmp;
 

	
 
  writer = xmlNewTextWriterFilename("general_info.xml", 0);
 
  xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL);
 

	
 
  xmlTextWriterStartElement(writer, (xmlChar*)"general_info");
 

	
 
  _distren_asprintf(&tmp, "%d", geninfo->jobs_in_queue);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"jobs_in_queue", (xmlChar*)tmp);
 
  _distren_asprintf(&tmp, "%d", geninfo->total_finished_jobs);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"total_finished_jobs", (xmlChar*)tmp);
 
  _distren_asprintf(&tmp, "%d", geninfo->total_frames_rendered);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"total_frames_rendered", (xmlChar*)tmp);
 
  _distren_asprintf(&tmp, "%d", geninfo->highest_jobnum);
 
  xmlTextWriterWriteElement(writer, (xmlChar*)"highest_jobnum", (xmlChar*)tmp);
 

	
 
  xmlTextWriterEndDocument(writer);
 
  xmlFreeTextWriter(writer);
 
}
 

	
 
// this reads the information from general_info.xml to the general_info structure
 
int import_general_info(struct general_info *general_info)
 
{
 
  xmlDocPtr doc;
 
  xmlNodePtr cur;
 

	
 
  doc = xmlParseFile("general_info.xml");
 
  cur = xmlDocGetRootElement(doc);
 
  if (xmlStrcmp(cur->name, (xmlChar*)"general_info"))
 
    {
 
      fprintf(stderr, "xml document is wrong type");
 
      xmlFreeDoc(doc);
 
      return 1;
 
    }
 

	
 
  cur = cur->xmlChildrenNode;
 
  general_info->jobs_in_queue = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 
  cur = cur->next;
 
  general_info->total_finished_jobs = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 
  cur = cur->next;
 
  general_info->total_frames_rendered = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 
  cur = cur->next;
 
  general_info->highest_jobnum = atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1));
 

	
 
  general_info->hibernate = 0;
 
  general_info->free_clients = 0;
 
  general_info->rendering_clients = 0;
 
  general_info->timestamp = 0;
 
  general_info->total_render_power = 0;
 
  general_info->total_priority_pieces = 0;
 

	
 
  xmlFreeDoc(doc);
 

	
 
  return 0;
 
}
 

	
 
// returns 1 on successful completion of xml file
 
// creates the xml file that slaves read from, and is used to restart distren
 
int makeJobDataXML(struct distrenjob *job)
 
{
 
  xmlTextWriterPtr writer;
 
  char *tmp; // temporarily holds strings to be given to the xml writer
 

	
 
  _distren_asprintf(&tmp, "stor/job%d/job_info.xml", job->jobnum);
 

	
 
  // create xml document at the location tmp with no compression
 
  writer = xmlNewTextWriterFilename(tmp, 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);
 

	
 
  // write resolution element and add its attributes
 
@@ -1024,193 +1040,194 @@ int printJob(struct distrenjob *job)
 

	
 
  return 1;
 
}
 

	
 
int printJobInfo(struct distrenjob *job)
 
{
 
  fprintf(stderr, "type: %d\n", job->type);
 
  fprintf(stderr, "name: %s\n", job->email);
 
  fprintf(stderr, "submitter: %s\n", job->submitter);
 
  fprintf(stderr, "e-mail: %s\n", job->email);
 
  fprintf(stderr, "priority: %d\n", job->priority);
 
  fprintf(stderr, "completed: %d\n", job->completed_frames);
 
  fprintf(stderr, "assigned: %d\n", job->assigned_frames);
 
  fprintf(stderr, "total: %d\n", job->total_frames);
 
  fprintf(stderr, "watchdog: %d\n", job->watchdog_forgiveness);
 
  fprintf(stderr, "hibernate: %d\n", job->hibernate);
 
  fprintf(stderr, "prev_frame: %d\n", job->prev_frame_index);
 
  fprintf(stderr, "render power: %ld\n", job->assigned_render_power);
 

	
 
  return 1;
 
}
 

	
 
int printAllJobnums(struct distrenjob *head)
 
{
 
  struct distrenjob *current_job;
 
  int total_jobs;
 
  fprintf(stderr, "job numbers in the order they will be processed:\n");
 

	
 
  total_jobs = 0;
 
  for(current_job = head->next; current_job; current_job = current_job->next)
 
    {
 
      fprintf(stderr, "%d: %s\n", current_job->jobnum, current_job->name);
 
      total_jobs++;
 
    }
 

	
 
  fprintf(stderr, "\n%d jobs in queue\n\n", total_jobs);
 

	
 
  return 1;
 
}
 

	
 
/* ************************** Main ************************* */
 

	
 
int main(int argc, char *argv[])
 
{
 

	
 
  /* Argument-parser */
 
  int counter;
 
  int test; // Interactive test mode if 1
 

	
 
  struct general_info general_info;
 

	
 
  int cont;
 
  struct distrend_clientset *clients;
 
  struct distrend_config *config;
 

	
 
  enum clientstatus
 
  {
 
    CLIENTSTATUS_UNINITIALIZED = 0,
 
    CLIENTSTATUS_BUSY = 1,
 
    CLIENTSTATUS_IDLE = 2
 
  } clientstatus;
 

	
 
  int command;
 
  jobnum_t jobnum;
 
  struct distrenjob *tmp_job;
 
  struct frameset *tmp_frame;
 
  int type;
 
  char *name;
 
  char *submitter;
 
  char *email;
 
  int priority;
 
  int width;
 
  int height;
 
  int start_frame;
 
  int end_frame;
 
  size_t read_buf;
 

	
 
  xmlinit();
 

	
 
  test = 0;
 
  for(counter = 0; counter < argc; counter ++)
 
    {
 
      if(strcmp(argv[counter], "-h") == 0)
 
      {
 
    	  fprintf(stderr, "Usage: distrend [option] \nStarts the distrend server\n\t-h\tshow this help\n\t-t\tlaunches queue testing interface \n");
 
		  return 2;
 
      }
 

	
 
      else if(strcmp(argv[counter], "-t") == 0)
 
      {
 
    	  fprintf(stderr, "Entering into test mode...\n\n");
 
		  test=1;
 
      }
 
    }
 
  cont = 1;
 

	
 
  distrend_do_config(argc, argv, &config);
 
  if(distrend_do_config(argc, argv, &config))
 
    return 1;
 

	
 
  if(start_data(&general_info, config->datadir))
 
    {
 
      fprintf(stderr, "%s:%d: start_data() failed\n", __FILE__, __LINE__);
 
      return 1;
 
    }
 

	
 
  // pre-loaded jobs for testing
 
  prepare_distrenjob(&general_info, 1, "awesome", "LordOfWar", "onlylordofwar@gmail.com", 8, 1, 100, 640, 480);
 
  prepare_distrenjob(&general_info, 1, "hamburger", "Ohnobinki", "ohnobinki@ohnopublishing.net", 3, 1, 50, 1280, 720);
 

	
 
  while(test == 1)
 
  {
 
    fprintf(stderr, "Welcome to DistRen Alpha Interactive Test Mode\n\n");
 
    fprintf(stderr, "\t1 \tPrint all frames in a job\n");
 
    fprintf(stderr, "\t2 \tExamine certain job\n");
 
    fprintf(stderr, "\t3 \tGet a frame to render\n");
 
    fprintf(stderr, "\t4 \tAdd a job\n");
 
    fprintf(stderr, "\t5 \tDelete a job\n");
 
    fprintf(stderr, "\t6 \tPrint jobnums in queue\n");
 
    fprintf(stderr, "\t7 \tPrint general info\n");
 
    fprintf(stderr, "\t8 \tQuit\n");
 

	
 
    scanf("%d", &command);
 

	
 
    switch(command)
 
    {
 
    case 1:
 
      fprintf(stderr, "Job number: ");
 
      scanf("%d", &jobnum);
 
      printJob(distrenjob_get(&general_info.head, jobnum));
 
      break;
 
    case 2:
 
      fprintf(stderr, "Job number: ");
 
      scanf("%d", &jobnum);
 
      printJobInfo(distrenjob_get(&general_info.head, jobnum));
 
      break;
 
    case 3:
 
      general_info.total_render_power ++;
 
      if(!find_jobframe_again(&general_info, -1, 1, &tmp_job, &tmp_frame))
 
      {
 
    	  fprintf(stderr, "frame was found, details below\n");
 
          fprintf(stderr, "Job#:%d\n", tmp_job->jobnum);
 
          fprintf(stderr, "Frame#:%d\n", tmp_frame->num);
 
      }
 

	
 
      break;
 
    case 4:
 
      name = NULL;
 
      submitter = NULL;
 
      email = NULL;
 

	
 
      fprintf(stderr, "\nType: \n\t 1 \t blender\n\t 2 \t povray\n"); scanf("%d", &type);
 
      fprintf(stderr, "\nName: ");        scanf("\n"); getline(&name, &read_buf, stdin);
 
      fprintf(stderr, "\nSubmitter: ");                getline(&submitter, &read_buf, stdin);
 
      fprintf(stderr, "\nEmail: ");                    getline(&email, &read_buf, stdin);
 
      fprintf(stderr, "\nPriority: ");                 scanf("%d", &priority);
 
      fprintf(stderr, "\nStart frame: ");              scanf("%d", &start_frame);
 
      fprintf(stderr, "\nEnd frame: ");                scanf("%d", &end_frame);
 
      fprintf(stderr, "\nWidth: ");                    scanf("%d", &width);
 
      fprintf(stderr, "\nHeight: ");                   scanf("%d", &height);
 
      prepare_distrenjob(&general_info, type, name, submitter, email, priority, start_frame, end_frame, width, height);
 
      break;
 
    case 5:
 
      fprintf(stderr, "\nJob number: ");
 
      scanf("%d", &jobnum);
 
      distrenjob_remove(&general_info, distrenjob_get(&general_info.head, jobnum));
 
      break;
 
    case 6:
 
      printAllJobnums(&general_info.head);
 
      break;
 
    case 7:
 
      fprintf(stderr, "\nHighest job number: %d", general_info.highest_jobnum);
 
      fprintf(stderr, "\nJobs in queue: %d", general_info.jobs_in_queue);
 
      fprintf(stderr, "\nTotal frames rendered: %d", general_info.total_frames_rendered);
 
      fprintf(stderr, "\nTimestamp: %lu", (long)general_info.timestamp);
 
      fprintf(stderr, "\nTotal priority pieces: %ld", general_info.total_priority_pieces);
 
      fprintf(stderr, "\nTotal render power: %ld\n", general_info.total_render_power);
 
      break;
 
    case 8:
 
      fprintf(stderr,"Goodbye.\n");
 
      return 0;
 
    default:
 
      fprintf(stderr, "Invalid input, please try again.\n");
 
    }
 
  }
 

	
 
  distrend_listen(config, &clients);
 
  /* This is called the "main loop" */
 
  while(cont)
 
    {
 
      struct distrend_action *action;
 
      int clientsays = 0; /*< temporary example variable, will be replaced when we can handle messages */
 

	
 
      distrend_accept(config, clients, &action);
 
      cont = distrend_do(action);
0 comments (0 inline, 0 general)