Logo Search packages:      
Sourcecode: yiff version File versions  Download package

prochandle.cpp

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <sched.h>
#include <signal.h>
#include <sys/time.h>

#ifndef _USE_BSD
# define _USE_BSD
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>

#include "../include/os.h"
#include "../include/string.h"
#include "../include/fio.h"
#include "../include/disk.h"
#include "../include/prochandle.h"

#ifdef MEMWATCH
# include "memwatch.h"
#endif


static void child_catcher(int s);
static void *add_signal(int signum, void (*handler)(int));

/* Explode commands. */
char **ExecExplodeCommand(const char *cmd, int *strc);

/* Process checking. */
int ExecProcessExists(pid_t pid);

/* Regular execute. */
void Execute(const char *cmd);

/* Non-blocking executes. */
static pid_t ExecNexus(
      const char *cmd, const char *stdout_path, const char *stderr_path,
      const char *stdout_mode, const char *stderr_mode
);
pid_t Exec(const char *cmd);
pid_t ExecO(const char *cmd, const char *stdout_path);
pid_t ExecOE(const char *cmd, const char *stdout_path, const char *stderr_path);
pid_t ExecAO(const char *cmd, const char *stdout_path);
pid_t ExecAOE(const char *cmd, const char *stdout_path, const char *stderr_path);

/* Blocking executes. */
static pid_t ExecBNexus(
      const char *cmd, const char *stdout_path, const char *stderr_path,
      const char *stdout_mode, const char *stderr_mode
);
pid_t ExecB(const char *cmd);
pid_t ExecBO(const char *cmd, const char *stdout_path);
pid_t ExecBOE(const char *cmd, const char *stdout_path, const char *stderr_path);
pid_t ExecBAO(const char *cmd, const char *stdout_path);
pid_t ExecBAOE(const char *cmd, const char *stdout_path, const char *stderr_path);



/*
 *    Signal handler for SIGCHLD.
 */
static void child_catcher(int s)
{
      int status;

      while(wait3(&status, WNOHANG, (struct rusage *)0) > 0);
}

/*
 *    BSD style signal(2).
 *
 *    Returns the old signal action handler of type
 *    void (*handler)(int).
 */
static void *add_signal(int signum, void (*handler)(int))
{
      struct sigaction act, old_act;

      act.sa_handler = handler;
      act.sa_flags = 0;

      if(sigaction(signum, &act, &old_act) == -1)
          return((void *)SIG_ERR);
      else
          return((void *)old_act.sa_handler);
}

/*
 *    Explodes the command string cmd and returns an array of
 *    dynamically allocated strings.
 *
 *    Arguments inside quotes ('"') with spaces will not be exploded.
 *
 *    Arguments will be delimited by one or more spaces (not tabs).
 *
 *    The calling function must free() the returned strings and the
 *    array.
 */
char **ExecExplodeCommand(const char *cmd, int *strc)
{
      int x, y, z, str_num, src_pos, len;
      char **strv;
      char skip_quotes;
      char c = ' ';     /* Deliminator. */


      if(strc == NULL)
          return(NULL);
      if(cmd == NULL)
      {
          (*strc) = 0;  
          return(NULL);
      }

      /* Reset values. */
      src_pos = 0;
      str_num = 0;
          
      strv = NULL;
      (*strc) = 0;

      len = strlen(cmd);
      skip_quotes = 0;

      /* Skip initial characters. */
      while(src_pos < len)
      {
          if(cmd[src_pos] != c)
            break;

          src_pos++;
      }

      /* Begin exploding strings. */
      while(src_pos < len)
      {
          skip_quotes = 0;

          /* Get length y of this segment x. */
          x = src_pos;
          y = 0;
          while((cmd[x] != c) && (x < len))
          {
            /* Quote grouping skip. */
            if(cmd[x] == '"')
            {
                skip_quotes = 1;
                x++;

                /* Seek to end quote. */
                while(x < len)
                {
                  if(cmd[x] == '"')
                      break;

                  x++;
                  y++;
                }

                break;
            }

            x++;
            y++;
          }
          /* y is now length of this segment. */

          /* Allocate new string for this segment. */
          str_num = (*strc);
          (*strc) = str_num + 1;
          strv = (char **)realloc(strv, (*strc) * sizeof(char *));
          strv[str_num] = (char *)calloc(1, (y + 1) * sizeof(char));

          /* Copy segment. */
          x = src_pos;
          z = 0;
          while((x < len) && (z < y))
          {
            /* Skip quotes. */
            if(cmd[x] == '"')
            {
                x++;
                continue;
            }

            strv[str_num][z] = cmd[x];
            z++;
            x++;
          }
          strv[str_num][y] = '\0';    /* Null terminate. */

          /* Seek next src_pos. */
          if(y < 1)
            src_pos += 1;
          else
            src_pos += y;
          if(skip_quotes)
          {
            while(src_pos < len)
            {
                if(cmd[src_pos] == '"')
                {
                  src_pos++;
                  break;
                }
                src_pos++;
            }
          }
          while(src_pos < len)
          {
            if(cmd[src_pos] != c)
                break;

            src_pos++;
          }
      }
      
      return(strv);
}


/*
 *      Returns true if the process is still running.
 */
int ExecProcessExists(pid_t pid)
{
#ifdef __linux__
      char path[PATH_MAX + NAME_MAX];
      sprintf(path, "/proc/%i", pid);
      return(access(path, F_OK) ? 0 : 1);
#else
      struct sched_param sp;


      /* Cannot be 0 (because it means itself to sched_getparam()). */
      if(pid == 0)
          return(0);

      if(sched_getparam(
          pid,
          &sp
      ) == 0)
          return(1);
      else
          return(0);
#endif
}


/*
 *    Old execute, fork()s off process and uses system() to execute
 *    command.
 *
 *    This function is provided for backwards compatability, but has
 *    security issues because it uses the unsafe system().
 */
void Execute(const char *cmd)
{
      if(cmd == NULL)
          return;

      /* Set signal to catch finished child proccesses. */
      add_signal(SIGCHLD, child_catcher);

      /* Fork off a process. */
      switch(fork())
      {
        /* Forking error. */
        case -1:
          perror("fork");
          exit(1);
          break;

        /* We are the child: run then command then exit. */  
        case 0:
          if(system(cmd) == -1)
            exit(-1);
          else
            exit(0);
          break;

        /* We are the parent: do nothing. */        
        default:
          break;
      }
}


/*              
 *      Main nexus for all Exec*() (non-blocking) functions.
 *
 *    If the given stdout_path and/or stderr_path are not NULL then 
 *    their respective files will be opened and the child's stdout 
 *    and/or stderr streams will be redirected to them.
 *
 *    The current working directory will be changed to the executed
 *    file's parent directory on the child process.
 */
static pid_t ExecNexus(
      const char *cmd,
      const char *stdout_path, const char *stderr_path,
      const char *stdout_mode, const char *stderr_mode
)
{
      int argc;
      char **argv;
      FILE *stdout_fp, *stderr_fp;
      pid_t p;


      if(cmd == NULL)
          return(0);

      /* Create stdout file (as needed). */
      if((stdout_path != NULL) && (stdout_mode != NULL))
          stdout_fp = FOpen(stdout_path, stdout_mode);
      else
          stdout_fp = NULL;

      /* Create stderr file (as needed). */
      if((stderr_path != NULL) && (stderr_mode != NULL))
          stderr_fp = FOpen(stderr_path, stderr_mode);
      else
          stderr_fp = NULL;

      /* Set stdout file stream options. */
      if(stdout_fp != NULL)
      {
          setlinebuf(stdout_fp);
      }
      /* Set stderr file stream options. */
      if(stderr_fp != NULL)
      {
          setlinebuf(stderr_fp);
      }

      /* Explode command string. */
      argv = ExecExplodeCommand(cmd, &argc);
      if((argv == NULL) || (argc < 1))
      {
          FClose(stdout_fp);
          FClose(stderr_fp);
          return(0);
      }

      /* Set last argument pointer to be NULL. */
      argv = (char **)realloc(argv, (argc + 1) * sizeof(char *));
      if(argv == NULL)
      {
          FClose(stdout_fp);
          FClose(stderr_fp);
          return(0);
      }
      argv[argc] = NULL;

      /* Object specified by command not executable? */
      if(access(argv[0], X_OK))
      {
          StringFreeArray(argv, argc);
          FClose(stdout_fp);
          FClose(stderr_fp);
          return(0);
      }

      /* Set signal to catch finished child proccesses. */
      add_signal(SIGCHLD, child_catcher);

      /* Fork off a process. */
      p = fork();
      switch(p)
      {
        /* Forking error. */
        case -1:
          perror("fork");
          exit(1);
          break;

        /* We are the child: run the command then exit. */
        case 0:
          /* Redirect child's stdout and stderr streams to our
           * opened output files (if any).
           */
          if(stdout_fp != NULL)
            dup2(fileno(stdout_fp), fileno(stdout));
          if(stderr_fp != NULL)
            dup2(fileno(stderr_fp), fileno(stderr));

          /* Execute command and arguments. */
          execvp(argv[0], argv);
          exit(0);
          break;

        /* We are the parent: Do nothing. */
        default:
          break;
      }

      /* Free exploded argument strings and array. */
      StringFreeArray(argv, argc);

      /* Close output files. */
      FClose(stdout_fp);
      FClose(stderr_fp);

      /* Return process id of child. */
      return(p);
}


pid_t Exec(const char *cmd)
{
      return(ExecNexus(cmd, NULL, NULL, NULL, NULL));
}

pid_t ExecO(const char *cmd, const char *stdout_path)
{
      return(ExecNexus(cmd, stdout_path, NULL, "w", NULL));
}

pid_t ExecOE(const char *cmd, const char *stdout_path, const char *stderr_path)
{
      return(ExecNexus(cmd, stdout_path, stderr_path, "w", "w"));
}

pid_t ExecAO(const char *cmd, const char *stdout_path)
{
      return(ExecNexus(cmd, stdout_path, NULL, "a", NULL));
}

pid_t ExecAOE(const char *cmd, const char *stdout_path, const char *stderr_path)
{
      return(ExecNexus(cmd, stdout_path, stderr_path, "a", "a"));
}


/*
 *      Main nexus for all ExecB*() (blocking) functions.
 *
 *      If the given stdout_path and/or stderr_path are not NULL then
 *      their respective files will be opened and the child's stdout
 *      and/or stderr streams will be redirected to them.
 *
 *      The current working directory will be changed to the executed
 *      file's parent directory on the child process.
 */
static pid_t ExecBNexus(
      const char *cmd,
      const char *stdout_path, const char *stderr_path,
      const char *stdout_mode, const char *stderr_mode
)
{
      int status, argc;
      char **argv;
      FILE *stdout_fp, *stderr_fp;
      pid_t p;


      if(cmd == NULL)
          return(0);

      /* Create stdout file (as needed). */
      if((stdout_path != NULL) && (stdout_mode != NULL))
          stdout_fp = FOpen(stdout_path, stdout_mode);
      else
          stdout_fp = NULL;

      /* Create stderr file (as needed). */
      if((stderr_path != NULL) && (stderr_mode != NULL))
          stderr_fp = FOpen(stderr_path, stderr_mode);
      else
          stderr_fp = NULL;

      /* Set stdout file stream options. */
      if(stdout_fp != NULL)
      {
          setlinebuf(stdout_fp);
      }
      /* Set stderr file stream options. */
      if(stderr_fp != NULL)
      {
          setlinebuf(stderr_fp);
      }

      /* Explode command string. */
      argv = ExecExplodeCommand(cmd, &argc);
      if((argv == NULL) || (argc < 1))
      {
          FClose(stdout_fp);
          FClose(stderr_fp);
          return(0);
      }

      /* Set last argument pointer to be NULL. */
      argv = (char **)realloc(argv, (argc + 1) * sizeof(char *));
      if(argv == NULL)
      {
          FClose(stdout_fp);
          FClose(stderr_fp);
          return(0);
      }
      argv[argc] = NULL;

      /* Object specified by command not executable? */
      if(access(argv[0], X_OK))
      {
          StringFreeArray(argv, argc);
          FClose(stdout_fp);
          FClose(stderr_fp);
          return(0);
      }

      /* Fork off a process. */
      p = fork();
      switch(p)
      {
        /* Forking error. */
        case -1:
          perror("fork");
          exit(1);
          break;

        /* We are the child: run the command then exit. */
        case 0:
          /* Redirect child's stdout and stderr streams to our
           * opened output files (if any).
           */
          if(stdout_fp != NULL)
              dup2(fileno(stdout_fp), fileno(stdout));
          if(stderr_fp != NULL)
            dup2(fileno(stderr_fp), fileno(stderr));

          /* Execute command and arguments. */
          execvp(argv[0], argv);
          exit(0);
          break;

        /* We are the parent: wait for child to finish. */
        default:
          wait(&status);
          break;
      }

      /* Free exploded argument strings and array. */
      StringFreeArray(argv, argc);

      /* Close output files. */
      FClose(stdout_fp);
      FClose(stderr_fp);

      return(p);
}

pid_t ExecB(const char *cmd)
{
      return(ExecBNexus(cmd, NULL, NULL, NULL, NULL));
}

pid_t ExecBO(const char *cmd, const char *stdout_path)
{
      return(ExecBNexus(cmd, stdout_path, NULL, "w", NULL));
}

pid_t ExecBOE(const char *cmd, const char *stdout_path, const char *stderr_path)
{
      return(ExecBNexus(cmd, stdout_path, stderr_path, "w", "w"));
}

pid_t ExecBAO(const char *cmd, const char *stdout_path)
{
      return(ExecBNexus(cmd, stdout_path, NULL, "a", NULL));
}

pid_t ExecBAOE(const char *cmd, const char *stdout_path, const char *stderr_path)
{
      return(ExecBNexus(cmd, stdout_path, stderr_path, "a", "a"));
}


Generated by  Doxygen 1.6.0   Back to index