Changeset - 34d3e7887adb
[Not reviewed]
default
0 1 0
Nathan Brink (binki) - 16 years ago 2009-04-08 16:09:42
ohnobinki@ohnopublishing.net
execio: cleaned up some comments
1 file changed with 2 insertions and 6 deletions:
0 comments (0 inline, 0 general)
src/common/execio.c
Show inline comments
 
@@ -9,126 +9,122 @@
 
  (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 "execio.h"
 

	
 
#include <unistd.h>
 
#include <sys/types.h>
 
#include <sys/wait.h>
 
#include <signal.h>
 
#include <malloc.h>
 
#include <fcntl.h>
 
#include <stdio.h>
 
#include <errno.h>
 

	
 
int execio_open(struct execio **rem, const char *progname, char *const argv[])
 
{
 
  /* pipe used to write to child */
 
  int pipe_write[2];
 
  /* pipe used to read from child */
 
  int pipe_read[2];
 

	
 
  pid_t child;
 

	
 
  /* for wait(2) if needed */
 
  int childstatus;
 
  
 
  int counter;
 
  int counter2;
 
  int maxfds;
 

	
 
  /* create two pipes to facilitate communication with child */
 
  if(pipe(pipe_write))
 
    return 1;
 
  if(pipe(pipe_read))
 
    {
 
      close(pipe_write[0]);
 
      close(pipe_write[1]);
 
      return 1;
 
    }
 
  
 
  /* make our side of the pipes nonblocking? I'll consider this type of stuff later and just keep this code in a comment like I always do... */
 
  //fcntl(pipe_write[1], F_SETFD, fcntl(pipe_write[1], F_GETFD) | O_NONBLOCK);
 
  //fucntl(pipe_read[0], F_SETFD, fcntl(pipe_read[0], F_GETFD) | O_NONBLOCK);
 
  
 
  /* parent */
 
  child = fork();
 
  if(child == -1)
 
    {
 
      close(pipe_write[0]);
 
      close(pipe_write[1]);
 
      close(pipe_read[0]);
 
      close(pipe_read[1]);
 
      return 1;
 
    }
 
  if(child)
 
    /* the parent proc: */
 
    {
 
      /* close sides of pipe we won't use */
 
      close(pipe_write[0]);
 
      close(pipe_read[1]);
 
      
 
      /* setup execio struct */
 
      (*rem) = malloc(sizeof(struct execio));
 
      if(!(*rem))
 
	{
 
	  /* we should tell the child we're dead - use wait and close our end of the pipes! */
 
	  close(pipe_write[1]);
 
	  close(pipe_read[0]);
 
	  /* we should probably pass of the wait() call to a thread that just does boring things like that. Especially for when the server tries to connect to other servers... */
 
	  /* maybe we should just kill the child */
 
	  /* maybe we should just kill instead of term the child */
 
	  kill(child, SIGTERM);
 
	  /* the waitpid(2) seems to indicate that only when the child is terminated will this wait return. */
 
	  waitpid(child, &childstatus, 0); 
 
	}
 
      (*rem)->pipe_write = pipe_write[1];
 
      (*rem)->pipe_read = pipe_read[0];
 
      (*rem)->state = 0;
 
      (*rem)->child = child;
 
      
 
      return 0;
 
    }
 
  
 
  /* child */
 
  else
 
    {
 
      /* close unused pipes */
 
      close(pipe_write[1]);
 
      close(pipe_read[0]);
 

	
 
      /*
 
	reset stdin, stdout, and stderr to the appropriate files. OK, not stderr :-) 
 
      */
 
      dup2(pipe_read[1], STDOUT_FILENO);
 
      dup2(pipe_write[0], STDIN_FILENO);
 
      /*
 
	close the fds that were dup'd
 
       */
 
      close(pipe_read[1]);
 
      close(pipe_write[0]);
 

	
 
      /* 
 
	 close all other file descriptors. We want to keep 0, 1, and 2 open. We don't know that the last open() or pipe() always gives the highest fd number. However, I will assume that it does. Maybe this is a bad idea:
 
       */
 
      counter = pipe_write[0];
 
      if(counter < pipe_write[1])
 
	counter = pipe_write[1];
 
      if(counter < pipe_read[0])
 
	counter = pipe_read[0];
 
      if(counter < pipe_read[1])
 
	counter = pipe_read[1];
 
      counter2 = 0;
 
      maxfds = counter;
 
      while(counter > 2)
 
	{
 
	  if(!close(counter))
 
	    counter2 ++; /* record how many descriptors we still had open :-) */
 
	  counter --;
 
	}
 
@@ -180,60 +176,60 @@ int execio_read(struct execio *eio, void
 

	
 
  return 0;
 
}
 

	
 
int execio_write(struct execio *eio, void *buf, size_t len, size_t *bytesread)
 
{
 
  errno = 0;
 
  (*bytesread) = write(eio->pipe_write, buf, len);
 
  if(!*bytesread)
 
    {
 
      switch(errno)
 
	{
 
	case EPIPE:
 
	  /* 
 
	     the program closed the pipe (died)
 
	  */
 
	fprintf(stderr, "execio_write: the child program closed its stdin pipe\n");
 
	eio->state = EXECIO_STATE_EOF;
 
	break;
 
	
 
	default:
 
	  fprintf(stderr, "execio_write: unhandled error writing to an fd: \n");
 
	  perror("write");
 
	  eio->state = EXECIO_STATE_ERROR;
 
	  break;
 
	  
 
	}
 
      
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 

	
 
enum execio_state execio_state(struct execio *eio)
 
{
 
  return eio->state;
 
}
 

	
 

	
 
int execio_close(struct execio *eio)
 
{
 
  int childstatus;
 

	
 
  close(eio->pipe_read);
 
  close(eio->pipe_write);
 

	
 
  /* maybe we should just kill the child */
 
  /* maybe we should just kill rather than term the child */
 
  kill(eio->child, SIGTERM);
 
  /* 
 
     the waitpid(2) seems to indicate that only when the child is terminated will this wait return. 
 
     This are of code will probably need improving - the ability to seng SIGKILL after a timeout? So we'll output a debug line before running waitpid
 
  */
 
  fprintf(stderr, "execio_close: running waitpid\n");
 
  waitpid(eio->child, &childstatus, 0);
 
  
 
  return 0;
 
}
 

	
0 comments (0 inline, 0 general)