Files @ 1ebfd45b11db
Branch filter:

Location: DistRen/src/server/slavefuncs.c

binki
convert ssh+remoteio to modular format
/*
  Copyright 2009 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 <http://www.gnu.org/licenses/>.
*/

 /*
  * Registration on server. Needs attention. Prevent account spamming.
  * distrenslave -c username email@example.com
 */

#include "asprintf.h"
#include "slavefuncs.h"
#include "distrenjob.h"
#include "execio.h"

#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>


/**
 utility function for XPath-ish stuff:
 */
xmlNodePtr xml_quickxpath(xmlXPathContextPtr xpathctxt, xmlChar *path)
{
  xmlNodePtr toreturn;

  xmlXPathObjectPtr xmlxpathobjptr;
  xmlxpathobjptr = xmlXPathEval(path, xpathctxt);
  if(!xmlxpathobjptr
     || !xmlxpathobjptr->nodesetval->nodeNr)
    {
      fprintf(stderr, "XPath resolution failed for ``%s'' in ``%s'' (``%s'')\n", path, xpathctxt->doc->name, xpathctxt->doc->URL);
      return (xmlNodePtr)NULL;
    }

  toreturn = *(xmlxpathobjptr->nodesetval->nodeTab);

  xmlXPathFreeObject(xmlxpathobjptr);

  return toreturn;
}


/** Stub stub stubbiness ugh */
void tell_the_server(char *stuff){
}

/** Function referenced by curlget() to write data to disk. */
size_t curl_writetodisk(void *ptr, size_t size, size_t nmemb, FILE *stream)
 {
    return fwrite(ptr, size, nmemb, stream);
  }

/** Gets a URL with cURL and saves it to disk */
int curlget(char *url, char *out){
  CURL *curl;
  CURLcode res;
  FILE *outfile;

  curl = curl_easy_init();
  if(curl) {
	outfile = fopen(out, "w"); // Open where we're writing to

  curl_easy_setopt(curl, CURLOPT_URL, url);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_writetodisk); // this MUST be set for win32 compat.
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);
  }
  return 0;
}

/** Posts a file to a url with cUrl */
int curlpost(char *filename, char *url){
  char *targetname = "uploadedfile"; // Name of the target in the php file
  CURL *curl;
  CURLcode res;
  struct curl_httppost *formpost=NULL;
  struct curl_httppost *lastptr=NULL;
  struct curl_slist *headerlist=NULL;
  static const char buf[] = "Expect:";

  curl_global_init(CURL_GLOBAL_ALL);

  /* upload field... */
  curl_formadd(&formpost,
               &lastptr,
               CURLFORM_COPYNAME, targetname,
               CURLFORM_FILE, filename,
               CURLFORM_END);
  /* filename field... */
  curl_formadd(&formpost,
               &lastptr,
               CURLFORM_COPYNAME, "filename",
               CURLFORM_COPYCONTENTS, filename,
               CURLFORM_END);
  /* submit field, not usually needed, just in case */
  curl_formadd(&formpost,
               &lastptr,
               CURLFORM_COPYNAME, "submit",
               CURLFORM_COPYCONTENTS, "send",
               CURLFORM_END);

  curl = curl_easy_init();
  headerlist = curl_slist_append(headerlist, buf);
  if(curl) {
    /* Setting the URL to get the post, and the contents of the post */
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
    res = curl_easy_perform(curl);

    curl_easy_cleanup(curl);
    /* cleanup the formpost junk */
    curl_formfree(formpost);
    curl_slist_free_all (headerlist);
  }
  return 0;
}

/** Generates a SSH key with ssh-keygen */
int ssh_keygen(){
	/* Checks to see if the keys are already present. */
	int status;
	struct stat buffer;
	status = stat(SYSCONFDIR "/distren.id_rsa", &buffer);
	if(status != -1){
		fprintf(stderr, "***Please delete etc/distren.id_rsa and etc/distren.id_rsa.pub to register.\n");
		return 0;
	}

	/* start execio code */
	char *command = "ssh-keygen"; // @TODO: append .exe if win32?
	int ret;
	char *cmd[] = { command, "-q", "-f", SYSCONFDIR "/distren.id_rsa", "-N", "", (char *)NULL }; // TODO: Give me the correct args!
	char buf[10];
	struct execio *testrem;
	size_t readlen;
	ret = execio_open(&testrem, command, cmd); // This path will be absolute for testing, should be relative to install on production
	buf[9] = '\0'; // null-terminating the array...
	while(!execio_read(testrem, buf, 9, &readlen))
	    {
	      if(readlen > 9) {
		fprintf(stderr, "Something is terribly wrong!\n");
	      }
	      buf[readlen] = '\0';
	      fprintf(stderr, "read \"%s\"\n", buf);
	    }
	execio_close(testrem);
	/* end execio code */

	// Supposedly execio returns 1 if it has bad args.
	if(ret == 1){
		fprintf(stderr, "Generating your key failed. Ensure that ssh-keygen is present!\n"); // Use different executor that searches the path? there is one...
		return 0;
	}
	else{
		fprintf(stderr,"We successfully generated your key! Yay!\n");
		return 1;
	}
return 0;
}

/** Registers the user on the DistRen server */
int register_user(char *username, char *email)
{

/* Note: this code moved here from after the useradd code, so useradd doesn't happen if there is an existing key, etc */
  /* puts the person's username in the conf */
  if(conf_replace(username) == 0){
	fprintf(stderr, "Failed!\n");
	return 0;
	}

  /* generates keys for login, @TODO: pub key must somehow be sent to the server. */
  if(ssh_keygen() == 0){
	  fprintf(stderr, "Failed!\n");
	  return 0;
  }

  /*
   * All created user accounts should be sandboxed accordingly, requiring a different skel, and the default shell to be rbash. Also,
   * a custom path defined in the .bashrc of the skel is needed.
   */
  char buf[10];
  struct execio *testrem;
  char *execargv[] =
    {
      "ssh",
      "distren_setup@protofusion.org",
      "-i",
      SYSCONFDIR "/setup_rsa", // @TODO: How will we distribute this key?
      "-p",
      "23",
      "newuser",
      "-M",
      "-c",
      email,
      "-d",
      "/home/distren",
      "--gid",
      "541",
      username,
      (char *)NULL
    };
  size_t readlen;
  execio_open(&testrem, "ssh", execargv); // TODO: Grab returns from this someday.
  buf[9] = '\0';
  while(!execio_read(testrem, buf, 9, &readlen))
    {
      if(readlen > 9) {
	fprintf(stderr, "!!!! Something is terribly wrong!\n");
      }
      buf[readlen] = '\0';
      fprintf(stderr, "read \"%s\"\n", buf);
    }
  execio_close(testrem);

  /* @TODO: Parse the output buffer or something to check when user creation fails due to duplicate users. This is pretty important. */

  return 1; // 1 is success
}

/** Logs the user into the server after ensuring that keys exist */
int login_user(char *username)
{
  char *userhost;
  userhost = malloc(strlen(username) + strlen("@protofusion.org") + 1);
  if(!userhost)
    return 43;
  strcpy(userhost, username);
  strcat(userhost, "@protofusion.org"); // Throws @protofusion.org after the username

  char buf[10];
  struct execio *testrem;
  char *execargv[] =
    {
      "ssh",
      userhost,// username and hostname
      "-i",
      SYSCONFDIR "/distren.id_rsa",
      "-p",
      "23",
      "echo",
      "hello", // This should eventually open a non-terminating connection to the server for communication,

      (char *)NULL
    };
  size_t readlen;
  fprintf(stderr, "Logging you in to %s\n", userhost);
  int status;
  struct stat buffer;
  status = stat(SYSCONFDIR "/distren.id_rsa", &buffer);
  if(status == -1){
	  fprintf(stderr,"Your key has not been found! Re-register or somehow regenerate your key!\nWe need a way to regenerate keys coded in, but we don't have the facilities yet!\n");
	  return 0;
  }
  execio_open(&testrem, "ssh", execargv); // TODO: Grab returns from this someday
  buf[9] = '\0'; // null-terminating the array...
  while(!execio_read(testrem, buf, 9, &readlen))
    {
      if(readlen > 9) {
	fprintf(stderr, "Something is terribly wrong!\n");
      }
    buf[readlen] = '\0';
    fprintf(stderr, "read \"%s\"\n", buf);
    }
  execio_close(testrem);
  return 1; // 1 means success
}

/** Replaces !username with a username in the slave's conf file */
int conf_replace(char *username){
  int maxlinelen = 120;
  char *fileOrig = SYSCONFDIR "/distrenslave.conf";
  char *fileRepl = SYSCONFDIR "/distrenslave.conf.edited";
  char *text2find = "!username";
  char *text2repl = username;
  char buffer[maxlinelen+2];
  char *buff_ptr, *find_ptr;
  FILE *fp1, *fp2;
  size_t find_len = strlen(text2find);
  fp1 = fopen(fileOrig,"r");
  fp2 = fopen(fileRepl,"w");
  if (fp1 ==NULL){
	  fprintf(stderr, "%s doesn't exist\n",fileOrig);
	  return 0;
  }
  else if(fp2 ==NULL){
	  fprintf(stderr, "Can't write a file to disk! Check permissions.\n");
	  return 0;
  }
  else{
    while(fgets(buffer,maxlinelen+2,fp1))
      {
          buff_ptr = buffer;
          while ((find_ptr = strstr(buff_ptr,text2find)))
          {
          while(buff_ptr < find_ptr)
          	fputc((int)*buff_ptr++,fp2);
          fputs(text2repl,fp2);
          buff_ptr += find_len;
          }
          fputs(buff_ptr,fp2);
      }
    rename(fileRepl, fileOrig);
  }
  fclose(fp2);
  fclose(fp1);
  fprintf(stderr,"Wrote conf file...\n");
return 1; // Success
}


/* Executors */

/*
  It seems that the client will need to know the job number. fixme.
*/

/** Executor function for Blender operations */
void exec_blender(struct distrenjob* distrenjob, char *input, char *output, int frame)
{
  int ret;
  char *frame_str;

  /* start execio code */
  char *command = "blender"; // @TODO: append .exe if win32?
  // @TODO: Put in code for output resolution, maybe... mayyyyyyybe....
  char *cmd[] = { command, "-b", "-o", output, input, "-f", frame_str, (char *)NULL };

  char buf[10];
  struct execio *testrem;
  size_t readlen;

  _distren_asprintf(&frame_str, "%i", frame);

  ret = execio_open(&testrem, command, cmd); // This path will be absolute for testing, should be relative to install on production
  buf[9] = '\0'; // null-terminating the array...
  while(!execio_read(testrem, buf, 9, &readlen))
  	    {
  	      if(readlen > 9) {
  		fprintf(stderr, "Something is terribly wrong!\n");
  	      }
  	      buf[readlen] = '\0';
  	      fprintf(stderr, "read \"%s\"\n", buf);
  	    }
  	execio_close(testrem);
  /* end execio code */
  	if(ret == 1){
  		fprintf(stderr,"Error starting Blender. Check your install.");
  	}
  	else{
  		fprintf(stderr,"Blender at least started nicely, who knows if it rendered anything though.");
  	}
}

void xmlinit()
{
  xmlInitParser();
  xmlXPathInit();
}

void xmlcleanup()
{
  xmlCleanupParser();
}