The Open Source Swiss Army Knife

/code/c/unix_c/fork/
/code/c/unix_c/fork/ + sub-categories
http://www.sirfsup.com/
web directory content
    
      

Not logged in
Chat Register Login
return to:  http:/www.sirfsup.com      /code   /c   /unix_c   /fork 
Permalink: child.c
Title: struct timeval tv; struct timezone tz; many typical unix programming constructs illustrated
article options : please login   |  raw source view  

#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>



#include "child.h"

/* Linux doesn't even bother to declare DST_NONE */
#ifndef DST_NONE
   #define DST_NONE 0
#endif

int child_debug=0;

void child_create(
   child_info_t *child_info_p,
   child_fp_t child_fp, 
   void *private_p 
) { 
   int rc;
   int fd;
   int seed;
   struct timeval tv;
   struct timezone tz;

   assert(child_info_p);
   assert(child_fp);

   /* struct timezone is obsolete and not really used */
   /* under Linux */
   tz.tz_minuteswest = 0;
   tz.tz_dsttime = DST_NONE;

   rc=fork();
   if(rc == (pid_t) -1) {
      /* error */
      perror("fork failed");
   } else if (rc>0) {
     /* parent */
     child_info_p->pid = rc;
   } else {
      #ifndef USING_SHARED_MEM
         child_info_p->pid = getpid();
         child_info_p->ppid = getppid();
      #endif

      /* reseed random number generator */
      /* if you don't do this in each child, they are likely to */
      /* all use the same random number stream */
      fd=open("/dev/random",O_RDONLY);
      assert(fd>=0);
      rc=read(fd, &seed, sizeof(seed));
      assert(rc == sizeof(seed));
      close(fd);
      srandom(seed);

      gettimeofday(&tv, NULL);
      fprintf(stderr, 
          "%010d.%06d: Starting child process #%d, pid=%d, parent=%d\n",
              tv.tv_sec,
              tv.tv_usec,
              child_info_p->number, 
              child_info_p->pid,
              child_info_p->ppid
      );

      child_info_p->child_fp = child_fp;

      child_fp(child_info_p, private_p);

      gettimeofday(&tv, NULL);
      fprintf(stderr, 
          "%010d.%06d: child process #%d finishing, pid=%d, parent=%d\n",
              tv.tv_sec,
              tv.tv_usec,
              child_info_p->number, 
              child_info_p->pid,
              child_info_p->ppid
      );

      /* child process ceases to exist here */
      exit(0);
   }
}


child_info_t *child_lookup_by_pid(
   const child_groups_t *child_groups_p,
   int pid
) {
   int i;
   int j;

   assert(child_groups_p);

   for(i=0; i<child_groups_p->ngroups; i++) {
      for(j=0; j<child_groups_p->group[i]->nchildren; j++) {
         if( child_groups_p->group[i]->child[j].pid == pid ) {
            return(&child_groups_p->group[i]->child[j]);
         }
      }
   }
   return(NULL);

}

int child_group_create(
   child_group_info_t *children_info_p, 
   const int min, 
   const int max, 
   const child_fp_t child_fp, 
   void *private_p 
) {
   int i; 

   children_info_p->nchildren = min;
   children_info_p->maxchildren = max;
   children_info_p->minchildren = min;

   for(i=0; i<min; i++) {
      children_info_p->child[i].number = i;
      children_info_p->child[i].child_fp = child_fp;

      child_create(&children_info_p->child[i],child_fp,private_p);
   }

   children_info_p->activechildren = min;

   return(0);
}


int child_restart_children = 1;

/* This function currently does not change the number of */
/* children.  In the future, it could be extended to change */
/* the number of children based on load.  Each time a child */
/* exited, it could restart 0, 1, or 2 children instead of 1 */

void child_groups_keepalive(
   const child_groups_t *child_groups_p
) {
   int rc;
   int child_status;
   int pid;
   child_info_t *child_p;

   while(1) {
      rc=wait(&child_status);
      if(child_restart_children==0) {
         fprintf(stderr,"child_groups_keepalive(): exiting\n");
         return;
      }
      if(rc>0) {
         fprintf(stderr,"wait() returned %d\n",rc);
         pid = rc;
         if(WIFEXITED(child_status)) {
            fprintf(stderr, "child exited normally\n");
         }
         if(WIFSIGNALED(child_status)) {
            fprintf(stderr, "child exited due to signal %d\n",
               WTERMSIG(child_status));
         }
         if(WIFSTOPPED(child_status)) {
            fprintf(stderr, "child suspended due to signal %d\n",
               WSTOPSIG(child_status));
         }

         /* Use kill with an argument of zero to see if */
         /* child still exists.  We could also use */
         /* results of WIFEXITED() and WIFSIGNALED */
         if(kill(pid,0)) {

            child_p = child_lookup_by_pid(child_groups_p, pid);
            assert(child_p);
            fprintf(stderr,
               "Child %d, pid %d, died, restarting\n", 
                child_p->pid, pid);

            child_create( child_p, child_p->child_fp, NULL );
         } else {
            fprintf(stderr,"Child pid %d still exists\n");
	 }   
      }
   }
}

int child_group_signal(
   child_group_info_t *children_info_p, 
   int signal
) {
   int i;
   int count;
   int rc;
   child_info_t *child_p;

   assert(children_info_p);
   assert(signal>=0);

   count=0;

   for(i=0; i<children_info_p->nchildren; i++) {
       child_p = &children_info_p->child[i];
       fprintf(stderr,"sending signal %d to pid %d\n",
          signal, child_p->pid);
       rc=kill(child_p->pid,signal);
       if(rc==0) count++;
   }
   return(count);
}

int child_groups_signal(
   const child_groups_t *child_groups_p,
   int signal
) {
   int i;
   int pid;
   int count;

   assert(child_groups_p);
   assert(signal>=0);
   count=0;
   for(i=0; i<child_groups_p->ngroups; i++) {
      count += child_group_signal(child_groups_p->group[i], signal);
   }
   return(count);
}

int child_groups_kill(
   const child_groups_t *child_groups_p
) {
   int child_count;
   int rc;
   int i;

   assert(child_groups_p);

   child_count=child_groups_signal(child_groups_p, 0);
   fprintf(stderr, "total children=%d\n", child_count);
   
   fprintf(stderr, "sending SIGTERM\n");
   child_groups_signal(child_groups_p, SIGTERM);

   /* wait up to 4 minutes for children to die */
   /* wait() may hang if children are already gone */
   for(i=0; i<24; i++) {
      rc=child_groups_signal(child_groups_p, 0);
      if(rc==child_count) return(child_count);
      sleep(5);
   } 
   
   fprintf(stderr, "some children did not die\n");
   fprintf(stderr, "sending SIGKILL\n");
   child_groups_signal(child_groups_p, SIGKILL);
}

/* debugging function for argv and envp */
void child_print_arg_array(char *name, char * const array[])
{
   int i;

   i=0;
   while(1) {
      if(array[i]) {
         fprintf(stderr,"%s[%d]=\"%s\"\n",name,i,array[i]);
      } else {
         fprintf(stderr,"%s[%d]=NULL\n",name,i,array[i]);
         break;
      }
      
      i++;    
   }
}



extern char **environ;

/* This function is intended as a replacement for */
/* system() and popen() which is more flexible and */
/* more secure.  The path to the executable must still */
/* be safe against write access by untrusted users. */

/* argv[] and envp[] must end with a NULL pointer */
/* stdin_fd_p, stdout_fd_p, or stderr_fd_p may be NULL, in */
/* which case, that stream will not be piped.  stdout_fd_p
/* and stderr_fd_p may be equal */ 
/* you may want to use fd_open() on the pipes to use stdio */
/* argv[0] should equal cmdpath */

int child_pipeve(
   const char *cmdpath,  /* full path to command */
   char * const argv[],   /* Array of pointers to arguments */
   char * const envp[],   /* Array of pointers to environment vars*/
   int *stdin_fd_p,      /* Output: fd for stdin pipe */
   int *stdout_fd_p,     /* Output: fd for stdout pipe */
   int *stderr_fd_p,     /* Output: fd for stderr pipe */
   int wait              /* wait for program termination */
) {
   int i;
   int rc;
   int pid;
   int status;
   int stdin_pipe[2];
   int stdout_pipe[2];
   int stderr_pipe[2];
   char *dummy_argv[8];
   char *dummy_envp[8];
 

   stdin_pipe[0] = -1;
   stdin_pipe[1] = -1;
   stdout_pipe[0] = -1;
   stdout_pipe[1] = -1;
   stderr_pipe[0] = -1;
   stderr_pipe[1] = -1;

   if(stdin_fd_p) {
      rc=pipe(stdin_pipe);
      if(rc!=0) return(-1);
      *stdin_fd_p = stdin_pipe[1];
   }

   if(stdout_fd_p) {
      rc=pipe(stdout_pipe);
      if(rc!=0) {
         if(stdin_pipe[0]>=0) close(stdin_pipe[0]);
         if(stdin_pipe[0]>=0) close(stdin_pipe[1]);
         return(-1);
      }
      *stdout_fd_p = stdout_pipe[0];
   }

   if(stderr_fd_p && (stderr_fd_p!=stdout_fd_p) ) {
      rc=pipe(stderr_pipe);
      if(rc!=0) {
         if(stdin_pipe[0]>=0) close(stdin_pipe[0]);
         if(stdin_pipe[0]>=0) close(stdin_pipe[1]);
         if(stdin_pipe[0]>=0) close(stdout_pipe[0]);
         if(stdin_pipe[0]>=0) close(stdout_pipe[1]);
         return(-1);
      }
      *stderr_fd_p = stderr_pipe[0];
   }


   rc=fork();
   if(rc<0) {
     /* error */
     return(-1);
   } else if(rc==0) {
     /* child */

     if(stdin_fd_p) {
        /* redirect stdin */
        rc=dup2(stdin_pipe[0],0);
     }

    if(stdout_fd_p) {
        /* redirect stdout */
        rc=dup2(stdout_pipe[1],1);
     }

    if(stderr_fd_p) {
        /* redirect stderr */
        if(stderr_fd_p == stdout_fd_p) {
           rc=dup2(stdout_pipe[1],2);
        } else {
           rc=dup2(stderr_pipe[1],2);
        } 
     }

     /* clean up file descriptors */
     #if 1
        for(i=3;i<OPEN_MAX;i++) {
           close(i);
        }
     #endif

     if(envp == NULL) envp = environ;


     if(child_debug>=5) {
        child_print_arg_array("argv",argv);
        child_print_arg_array("envp",envp);
        fprintf(stderr,"cmdpath=\"%s\"\n",cmdpath);
     }

     fprintf(stderr,"about to execve()\n");

     #if 1
        execve(cmdpath, argv, envp);
     #else
        dummy_argv[0] = "./child_demo4";
        dummy_argv[1] = "one";
        dummy_argv[2] = "two";
        dummy_argv[3] = "three";
        dummy_argv[4] = NULL;
        dummy_envp[0] = "PATH=/bin:/usr/bin";
        dummy_envp[1] = NULL;

        execve("./child_demo4",dummy_argv,dummy_envp);
     #endif
     /* we should never get here unless error */

     fprintf(stderr, "execve() failed\n");
     perror("execve()");
     /* we will be lazy and let process termination */
     /* clean up open file descriptors */
     exit(255);

   } else {
     /* parent */
     pid=rc;

     /* If we leave these open in parent, we will never get */
     /* EOF on pipe when child is done */
     close(stdin_pipe[0]);
     close(stdout_pipe[1]);
     close(stderr_pipe[1]);

     if(wait) {
        rc=waitpid(pid, &status, 0);

        if(rc<0) {
	   /* waitpid() set errno */
	   return(-1);
	}
	if(WIFEXITED(status)) {
	   fprintf(stderr,"child_pipve(): child exited normally\n");
	   return( (int) (signed char) WEXITSTATUS(status));
	} else if(WIFSIGNALED(status)) {
	   fprintf(stderr,"child_pipve(): child caught signal\n");
	   errno = EINTR;
	   return(-1);
	} else {
	   fprintf(stderr,"child_pipve(): unkown child status\n");
	   /* we should handle stopped processes better */
	   errno = EINTR;
	   return(-1);
	}
        return(rc);
     } else {
        /* don't wait */
        return(pid);
     }
   }


}


void child_init()
{
   ;
}

void child_term()
{
   ;
}














Leave a Reply
Your Name:     anonymous
Your Email:
Website:  
Comments:

The author will be notified of your reply.
return to top