// $Id: Process.cpp 97594 2014-02-17 12:09:55Z johnnyw $

#include "ace/Process.h"

#if !defined (__ACE_INLINE__)
#include "ace/Process.inl"
#endif /* __ACE_INLINE__ */

#include "ace/ARGV.h"
#include "ace/Auto_Ptr.h"
#include "ace/Signal.h"
#include "ace/SString.h"
#include "ace/Log_Category.h"
#include "ace/OS_NS_stdio.h"
#include "ace/OS_NS_stdlib.h"
#include "ace/OS_NS_sys_socket.h"
#include "ace/OS_NS_errno.h"
#include "ace/OS_NS_string.h"
#include "ace/OS_NS_unistd.h"
#include "ace/OS_NS_fcntl.h"
#include "ace/OS_Memory.h"
#include "ace/Countdown_Time.h"
#include "ace/Truncate.h"
#include "ace/Vector_T.h"
#include "ace/Tokenizer_T.h"

#if defined (ACE_VXWORKS) && defined (__RTP__)
# include <rtpLib.h>
# include <taskLib.h>
#endif

// This function acts as a signal handler for SIGCHLD. We don't really want
// to do anything with the signal - it's just needed to interrupt a sleep.
// See wait() for more info.
#if !defined (ACE_WIN32) && !defined(ACE_LACKS_UNIX_SIGNALS)
static void
sigchld_nop (int, siginfo_t *, ucontext_t *)
{
  return;
}
#endif /* ACE_WIN32 */


ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_Process::ACE_Process (void)
  :
#if !defined (ACE_WIN32)
  child_id_ (ACE_INVALID_PID),
#endif /* !defined (ACE_WIN32) */
  exit_code_ (0)
{
#if defined (ACE_WIN32)
  ACE_OS::memset ((void *) &this->process_info_,
                  0,
                  sizeof this->process_info_);
#endif /* ACE_WIN32 */
}

ACE_Process::~ACE_Process (void)
{
#if defined (ACE_WIN32)
  // Free resources allocated in kernel.
  ACE_OS::close (this->process_info_.hThread);
  ACE_OS::close (this->process_info_.hProcess);
#endif /* ACE_WIN32 */
  // If any handles were duplicated for the child process and
  // still not closed, get them now.
  this->close_dup_handles ();
}

int
ACE_Process::prepare (ACE_Process_Options &)
{
  return 0;
}

pid_t
ACE_Process::spawn (ACE_Process_Options &options)
{
  if (this->prepare (options) < 0)
    return ACE_INVALID_PID;

  // Stash the passed/duped handle sets away in this object for later
  // closing if needed or requested. At the same time, figure out which
  // ones to include in command line options if that's needed below.
  ACE_Handle_Set *set_p = 0;
  if (options.dup_handles (this->dup_handles_))
    set_p = &this->dup_handles_;
  else if (options.passed_handles (this->handles_passed_))
    set_p = &this->handles_passed_;

  // If we are going to end up running a new program (i.e. Win32, or
  // NO_EXEC option is set) then get any handles passed in the options,
  // and tack them onto the command line with +H <handle> options,
  // unless the command line runs out of space.
  // Note that we're using the knowledge that all the options, argvs, etc.
  // passed to the options are all sitting in the command_line_buf. Any
  // call to get the argv then splits them out. So, regardless of the
  // platform, tack them all onto the command line buf and take it
  // from there.
  if (set_p && !ACE_BIT_ENABLED (options.creation_flags (),
                                 ACE_Process_Options::NO_EXEC))
    {
      size_t max_len = 0;
      ACE_TCHAR *cmd_line_buf = options.command_line_buf (&max_len);
      size_t curr_len = ACE_OS::strlen (cmd_line_buf);
      ACE_Handle_Set_Iterator h_iter (*set_p);
      // Because the length of the to-be-formatted +H option is not
      // known, and we don't have a snprintf, guess at the space
      // needed (20 chars), and use that as a limit.
      for (ACE_HANDLE h = h_iter ();
           h != ACE_INVALID_HANDLE && curr_len + 20 < max_len;
           h = h_iter ())
        {
#if defined (ACE_WIN32)
# if defined (ACE_WIN64)
// silence warnings coming from MinGW64 compilers
#  if defined (__GNUC__)
#   pragma GCC diagnostic push
#   pragma GCC diagnostic ignored "-Wformat"
#   pragma GCC diagnostic ignored "-Wformat-extra-args"
#  endif /* __GNUC__ */
          curr_len += ACE_OS::sprintf (&cmd_line_buf[curr_len],
                                       ACE_TEXT (" +H %I64p"),
                                       h);
#  if defined (__GNUC__)
#   pragma GCC diagnostic pop
#  endif /* __GNUC__ */
# else
          curr_len += ACE_OS::sprintf (&cmd_line_buf[curr_len],
                                       ACE_TEXT (" +H %p"),
                                       h);
# endif  /* ACE_WIN64 */
#else
          curr_len += ACE_OS::sprintf (&cmd_line_buf[curr_len],
                                       ACE_TEXT (" +H %d"),
                                       h);
#endif /* ACE_WIN32 */
        }
    }

#if defined (ACE_HAS_WINCE)
  // Note that WinCE does not have process name included in the command line as argv[0]
  // like other OS environment.  Therefore, it is user's whole responsibility to call
  // 'ACE_Process_Options::process_name(const ACE_TCHAR *name)' to set the proper
  // process name (the execution file name with path if needed).
  BOOL fork_result =
    ACE_TEXT_CreateProcess (options.process_name(),
                            options.command_line_buf(),
                            options.get_process_attributes(),  // must be NULL in CE
                            options.get_thread_attributes(),   // must be NULL in CE
                            options.handle_inheritance(),      // must be false in CE
                            options.creation_flags(),          // must be NULL in CE
                            options.env_buf(),                 // environment variables, must be NULL in CE
                            options.working_directory(),       // must be NULL in CE
                            options.startup_info(),            // must be NULL in CE
                            &this->process_info_);

  if (fork_result)
    {
      parent (this->getpid ());
      return this->getpid ();
    }
  return ACE_INVALID_PID;

#elif defined (ACE_WIN32)
  void* env_buf = options.env_buf ();
  DWORD flags = options.creation_flags ();
# if defined (ACE_HAS_WCHAR) && !defined (ACE_USES_WCHAR)
  wchar_t* wenv_buf = 0;
  if (options.use_unicode_environment ())
    {
      wenv_buf = this->convert_env_buffer (options.env_buf ());
      env_buf = wenv_buf;
      flags |= CREATE_UNICODE_ENVIRONMENT;
    }
# endif

  BOOL fork_result =
    ACE_TEXT_CreateProcess (0,
                            options.command_line_buf (),
                            options.get_process_attributes (),
                            options.get_thread_attributes (),
                            options.handle_inheritance (),
                            flags,
                            env_buf, // environment variables
                            options.working_directory (),
                            options.startup_info (),
                            &this->process_info_);

# if defined (ACE_HAS_WCHAR) && !defined (ACE_USES_WCHAR)
  if (options.use_unicode_environment ())
    delete wenv_buf;
# endif

  if (fork_result)
    {
      parent (this->getpid ());
      return this->getpid ();
    }
  return ACE_INVALID_PID;

#elif defined(ACE_OPENVMS)
  if (ACE_BIT_ENABLED (options.creation_flags (),
                       ACE_Process_Options::NO_EXEC))
    ACE_NOTSUP_RETURN (ACE_INVALID_PID);

  int saved_stdin = ACE_STDIN;
  int saved_stdout = ACE_STDOUT;
  int saved_stderr = ACE_STDERR;
  // Save STD file descriptors and redirect
  if (options.get_stdin () != ACE_INVALID_HANDLE) {
    if ((saved_stdin = ACE_OS::dup (ACE_STDIN)) == -1 && errno != EBADF)
      ACE_OS::exit (errno);
    if (ACE_OS::dup2 (options.get_stdin (), ACE_STDIN) == -1)
      ACE_OS::exit (errno);
  }
  if (options.get_stdout () != ACE_INVALID_HANDLE) {
    if ((saved_stdout = ACE_OS::dup (ACE_STDOUT)) == -1 && errno != EBADF)
      ACE_OS::exit (errno);
    if (ACE_OS::dup2 (options.get_stdout (), ACE_STDOUT) == -1)
      ACE_OS::exit (errno);
  }
  if (options.get_stderr () != ACE_INVALID_HANDLE) {
    if ((saved_stderr = ACE_OS::dup (ACE_STDERR)) == -1 && errno != EBADF)
      ACE_OS::exit (errno);
    if (ACE_OS::dup2 (options.get_stderr (), ACE_STDERR) == -1)
      ACE_OS::exit (errno);
  }

  if (options.working_directory () != 0)
    ACE_NOTSUP_RETURN (ACE_INVALID_PID);

  this->child_id_ = vfork();
  if (this->child_id_ == 0) {
      ACE_OS::execvp (options.process_name (),
                options.command_line_argv ());
      // something went wrong
      this->child_id_ = ACE_INVALID_PID;
  }

  // restore STD file descriptors (if necessary)
  if (options.get_stdin () != ACE_INVALID_HANDLE) {
    if (saved_stdin == -1)
      ACE_OS::close (ACE_STDIN);
    else
      ACE_OS::dup2 (saved_stdin, ACE_STDIN);
  }
  if (options.get_stdout () != ACE_INVALID_HANDLE) {
    if (saved_stdout == -1)
      ACE_OS::close (ACE_STDOUT);
    else
      ACE_OS::dup2 (saved_stdout, ACE_STDOUT);
  }
  if (options.get_stderr () != ACE_INVALID_HANDLE) {
    if (saved_stderr == -1)
      ACE_OS::close (ACE_STDERR);
    else
      ACE_OS::dup2 (saved_stderr, ACE_STDERR);
  }

  return this->child_id_;
#elif defined (ACE_VXWORKS) && defined (__RTP__)
  if (ACE_BIT_ENABLED (options.creation_flags (),
                       ACE_Process_Options::NO_EXEC))
    ACE_NOTSUP_RETURN (ACE_INVALID_PID);

  if (options.working_directory () != 0)
    ACE_NOTSUP_RETURN (ACE_INVALID_PID);

  int saved_stdin = ACE_STDIN;
  int saved_stdout = ACE_STDOUT;
  int saved_stderr = ACE_STDERR;
  // Save STD file descriptors and redirect
  if (options.get_stdin () != ACE_INVALID_HANDLE) {
    if ((saved_stdin = ACE_OS::dup (ACE_STDIN)) == -1 && errno != EBADF)
      ACE_OS::exit (errno);
    if (ACE_OS::dup2 (options.get_stdin (), ACE_STDIN) == -1)
      ACE_OS::exit (errno);
  }
  if (options.get_stdout () != ACE_INVALID_HANDLE) {
    if ((saved_stdout = ACE_OS::dup (ACE_STDOUT)) == -1 && errno != EBADF)
      ACE_OS::exit (errno);
    if (ACE_OS::dup2 (options.get_stdout (), ACE_STDOUT) == -1)
      ACE_OS::exit (errno);
  }
  if (options.get_stderr () != ACE_INVALID_HANDLE) {
    if ((saved_stderr = ACE_OS::dup (ACE_STDERR)) == -1 && errno != EBADF)
      ACE_OS::exit (errno);
    if (ACE_OS::dup2 (options.get_stderr (), ACE_STDERR) == -1)
      ACE_OS::exit (errno);
  }

  // Wide-char builds need narrow-char strings for commandline and
  // environment variables.
# if defined (ACE_USES_WCHAR)
  wchar_t * const *wargv = options.command_line_argv ();
  size_t vcount, i;
  for (vcount = 0; wargv[vcount] != 0; ++vcount)
    ;
  char **procargv = new char *[vcount + 1];  // Need 0 at the end
  procargv[vcount] = 0;
  for (i = 0; i < vcount; ++i)
    procargv[i] = ACE_Wide_To_Ascii::convert (wargv[i]);

  char **procenv = 0;
  if (options.inherit_environment ())
    {
      wargv = options.env_argv ();
      for (vcount = 0; wargv[vcount] != 0; ++vcount)
        ;
      procenv = new char *[vcount + 1];  // Need 0 at the end
      procenv[vcount] = 0;
      for (i = 0; i < vcount; ++i)
        procenv[i] = ACE_Wide_To_Ascii::convert (wargv[i]);
    }
# else
  const char **procargv = const_cast<const char**> (options.command_line_argv ());
  const char **procenv = const_cast<const char**> (options.env_argv ());
# endif /* ACE_USES_WCHAR */

  this->child_id_ = ::rtpSpawn (procargv[0],
                                procargv,
                                procenv,
                                200,          // priority
                                0x10000,      // uStackSize
                                0,            // options
                                VX_FP_TASK);  // taskOptions
  int my_errno_ = errno;
  if (this->child_id_ == ERROR) {
      // something went wrong
      this->child_id_ = ACE_INVALID_PID;
  }

# if defined (ACE_USES_WCHAR)
  if (procenv)
    delete procenv;
# endif /* ACE_USES_WCHAR */

  // restore STD file descriptors (if necessary)
  if (options.get_stdin () != ACE_INVALID_HANDLE) {
    if (saved_stdin == -1)
      ACE_OS::close (ACE_STDIN);
    else
      ACE_OS::dup2 (saved_stdin, ACE_STDIN);
  }
  if (options.get_stdout () != ACE_INVALID_HANDLE) {
    if (saved_stdout == -1)
      ACE_OS::close (ACE_STDOUT);
    else
      ACE_OS::dup2 (saved_stdout, ACE_STDOUT);
  }
  if (options.get_stderr () != ACE_INVALID_HANDLE) {
    if (saved_stderr == -1)
      ACE_OS::close (ACE_STDERR);
    else
      ACE_OS::dup2 (saved_stderr, ACE_STDERR);
  }

  if (this->child_id_ == ACE_INVALID_PID)
    {
      errno = my_errno_;
    }

  return this->child_id_;
#else /* ACE_WIN32 */
  // Fork the new process.
  this->child_id_ = ACE::fork (options.process_name (),
                               options.avoid_zombies ());

  if (this->child_id_ == 0)
    {
# if !defined (ACE_LACKS_SETPGID)
      // If we're the child and the options specified a non-default
      // process group, try to set our pgid to it.  This allows the
      // <ACE_Process_Manager> to wait for processes by their
      // process-group.
      if (options.getgroup () != ACE_INVALID_PID
          && ACE_OS::setpgid (0,
                              options.getgroup ()) < 0)
        {
#if !defined (ACE_HAS_THREADS)
          // We can't emit this log message because ACELIB_ERROR(), etc.
          // will invoke async signal unsafe functions, which results
          // in undefined behavior in threaded programs.
          ACELIB_ERROR ((LM_ERROR,
                      ACE_TEXT ("%p.\n"),
                      ACE_TEXT ("ACE_Process::spawn: setpgid failed.")));
#endif
        }
# endif /* ACE_LACKS_SETPGID */

# if !defined (ACE_LACKS_SETREGID)
      if (options.getrgid () != (uid_t) -1
          || options.getegid () != (uid_t) -1)
        if (ACE_OS::setregid (options.getrgid (),
                              options.getegid ()) == -1)
          {
#if !defined (ACE_HAS_THREADS)
            // We can't emit this log message because ACELIB_ERROR(), etc.
            // will invoke async signal unsafe functions, which results
            // in undefined behavior in threaded programs.
            ACELIB_ERROR ((LM_ERROR,
                        ACE_TEXT ("%p.\n"),
                        ACE_TEXT ("ACE_Process::spawn: setregid failed.")));
#endif
          }
# endif /* ACE_LACKS_SETREGID */

# if !defined (ACE_LACKS_SETREUID)
      // Set user and group id's.
      if (options.getruid () != (uid_t) -1
          || options.geteuid () != (uid_t) -1)
        if (ACE_OS::setreuid (options.getruid (),
                              options.geteuid ()) == -1)
          {
#if !defined (ACE_HAS_THREADS)
            // We can't emit this log message because ACELIB_ERROR(), etc.
            // will invoke async signal unsafe functions, which results
            // in undefined behavior in threaded programs.
            ACELIB_ERROR ((LM_ERROR,
                        ACE_TEXT ("%p.\n"),
                        ACE_TEXT ("ACE_Process::spawn: setreuid failed.")));
#endif
          }
# endif /* ACE_LACKS_SETREUID */

      this->child (ACE_OS::getppid ());
    }
  else if (this->child_id_ != -1)
    this->parent (this->child_id_);

  // If we're not supposed to exec, return the process id.
  if (ACE_BIT_ENABLED (options.creation_flags (),
                       ACE_Process_Options::NO_EXEC))
    return this->child_id_;

  switch (this->child_id_)
    {
    case -1:
      // Error.
      return ACE_INVALID_PID;
    case 0:
      // Child process...exec the
      {
        if (options.get_stdin () != ACE_INVALID_HANDLE
            && ACE_OS::dup2 (options.get_stdin (),
                             ACE_STDIN) == -1)
          ACE_OS::exit (errno);
        else if (options.get_stdout () != ACE_INVALID_HANDLE
                 && ACE_OS::dup2 (options.get_stdout (),
                                  ACE_STDOUT) == -1)
          ACE_OS::exit (errno);
        else if (options.get_stderr () != ACE_INVALID_HANDLE
                 && ACE_OS::dup2 (options.get_stderr (),
                                  ACE_STDERR) == -1)
          ACE_OS::exit (errno);

        // close down unneeded descriptors
        ACE_OS::close (options.get_stdin ());
        ACE_OS::close (options.get_stdout ());
        ACE_OS::close (options.get_stderr ());
        if (!options.handle_inheritance ())
          {
            // Set close-on-exec for all FDs except standard handles
            for (int i = ACE::max_handles () - 1; i >= 0; i--)
              {
                if (i == ACE_STDIN || i == ACE_STDOUT || i == ACE_STDERR)
                  continue;
                ACE_OS::fcntl (i, F_SETFD, FD_CLOEXEC);
              }
          }

        // If we must, set the working directory for the child
        // process.
        if (options.working_directory () != 0)
          ACE_OS::chdir (options.working_directory ());
        // Should check for error here!

        // Child process executes the command.
        int result = 0;

        // Wide-char builds not on Windows need narrow-char strings for
        // exec() and environment variables. Don't need to worry about
        // releasing any of the converted string memory since this
        // process will either exec() or exit() shortly.
# if defined (ACE_USES_WCHAR)
        ACE_Wide_To_Ascii n_procname (options.process_name ());
        const char *procname = n_procname.char_rep ();

        wchar_t * const *wargv = options.command_line_argv ();
        size_t vcount, i;
        for (vcount = 0; wargv[vcount] != 0; ++vcount)
          ;
        char **procargv = new char *[vcount + 1];  // Need 0 at the end
        procargv[vcount] = 0;
        for (i = 0; i < vcount; ++i)
          procargv[i] = ACE_Wide_To_Ascii::convert (wargv[i]);

        wargv = options.env_argv ();
        for (vcount = 0; wargv[vcount] != 0; ++vcount)
          ;
        char **procenv = new char *[vcount + 1];  // Need 0 at the end
        procenv[vcount] = 0;
        for (i = 0; i < vcount; ++i)
          procenv[i] = ACE_Wide_To_Ascii::convert (wargv[i]);
# else
        const char *procname = options.process_name ();
        char *const *procargv = options.command_line_argv ();
        char *const *procenv = options.env_argv ();
# endif /* ACE_USES_WCHAR */

        if (options.inherit_environment ())
          {
            // Add the new environment variables to the environment
            // context of the context before doing an <execvp>.
            for (size_t i = 0; procenv[i] != 0; i++)
              if (ACE_OS::putenv (procenv[i]) != 0)
                return ACE_INVALID_PID;

            // Now the forked process has both inherited variables and
            // the user's supplied variables.
            result = ACE_OS::execvp (procname, procargv);
          }
        else
          {
            result = ACE_OS::execve (procname, procargv, procenv);
          }
        if (result == -1)
          {
            // If the execv fails, this child needs to exit.

            // Exit with the errno so that the calling process can
            // catch this and figure out what went wrong.
            ACE_OS::_exit (errno);
          }
        // ... otherwise, this is never reached.
        return 0;
      }
    default:
      // Server process.  The fork succeeded.
      return this->child_id_;
    }
#endif /* ACE_WIN32 */
}

void
ACE_Process::parent (pid_t)
{
  // nothing to do
}

void
ACE_Process::child (pid_t)
{
  // nothing to do
}

void
ACE_Process::unmanage (void)
{
  // nothing to do
}

int
ACE_Process::running (void) const
{
#if defined (ACE_WIN32)
    DWORD code;

    BOOL result = ::GetExitCodeProcess (this->gethandle (),
                                        &code);
    return result && code == STILL_ACTIVE;
#else
  if (ACE_INVALID_PID == this->getpid ())
    return 0;
  else
    return ACE_OS::kill (this->getpid (),
                         0) == 0
      || errno != ESRCH;
#endif /* ACE_WIN32 */
}

pid_t
ACE_Process::wait (const ACE_Time_Value &tv,
                   ACE_exitcode *status)
{
#if defined (ACE_WIN32)
  // Don't try to get the process exit status if wait failed so we can
  // keep the original error code intact.
  switch (::WaitForSingleObject (process_info_.hProcess,
                                 tv.msec ()))
    {
    case WAIT_OBJECT_0:
        // The error status of <GetExitCodeProcess> is nonetheless not
        // tested because we don't know how to return the value.
        ::GetExitCodeProcess (process_info_.hProcess,
                              &this->exit_code_);
      if (status != 0)
        *status = this->exit_code_;
      return this->getpid ();
    case WAIT_TIMEOUT:
      errno = ETIME;
      return 0;
    default:
      ACE_OS::set_errno_to_last_error ();
      return -1;
    }
#elif defined(ACE_LACKS_UNIX_SIGNALS)
  if (tv == ACE_Time_Value::zero)
    {
      pid_t retv =
        ACE_OS::waitpid (this->child_id_,
                         &this->exit_code_,
                         WNOHANG);
      if (status != 0)
        *status = this->exit_code_;

      return retv;
    }

  if (tv == ACE_Time_Value::max_time)
# if defined (ACE_VXWORKS)
    {
      pid_t retv;
      while ((retv = this->wait (status)) == ACE_INVALID_PID && errno == EINTR);
      return retv;
    }
# else
     return this->wait (status);
# endif

  pid_t pid = 0;
  ACE_Time_Value sleeptm (1);    // 1 msec
  if (sleeptm > tv)              // if sleeptime > waittime
      sleeptm = tv;
  ACE_Time_Value tmo (tv);       // Need one we can change
  for (ACE_Countdown_Time time_left (&tmo); tmo > ACE_Time_Value::zero ; time_left.update ())
    {
      pid = ACE_OS::waitpid (this->getpid (),
                             &this->exit_code_,
                             WNOHANG);
      if (status != 0)
        *status = this->exit_code_;

      if (pid > 0 || pid == ACE_INVALID_PID)
        break;          // Got a child or an error - all done

      // pid 0, nothing is ready yet, so wait.
      // Do a (very) short sleep (only this thread sleeps).
      ACE_OS::sleep (sleeptm);
    }

  return pid;
#else /* !ACE_WIN32 && !ACE_LACKS_UNIX_SIGNALS */
  if (tv == ACE_Time_Value::zero)
    {
      pid_t retv =
        ACE_OS::waitpid (this->child_id_,
                         &this->exit_code_,
                         WNOHANG);
      if (status != 0)
        *status = this->exit_code_;

      return retv;
    }

  if (tv == ACE_Time_Value::max_time)
    return this->wait (status);

  // Need to wait but limited to specified time.
  // Force generation of SIGCHLD, even though we don't want to
  // catch it - just need it to interrupt the sleep below.
  // If this object has a reactor set, assume it was given at
  // open(), and there's already a SIGCHLD action set, so no
  // action is needed here.
  ACE_Sig_Action old_action;
  ACE_Sig_Action do_sigchld ((ACE_SignalHandler)sigchld_nop);
  do_sigchld.register_action (SIGCHLD, &old_action);

  pid_t pid;
  ACE_Time_Value tmo (tv);       // Need one we can change
  for (ACE_Countdown_Time time_left (&tmo); ; time_left.update ())
    {
      pid = ACE_OS::waitpid (this->getpid (),
                             &this->exit_code_,
                             WNOHANG);
      if (status != 0)
        *status = this->exit_code_;

      if (pid > 0 || pid == ACE_INVALID_PID)
        break;          // Got a child or an error - all done

      // pid 0, nothing is ready yet, so wait.
      // Do a sleep (only this thread sleeps) til something
      // happens. This relies on SIGCHLD interrupting the sleep.
      // If SIGCHLD isn't delivered, we'll need to do something
      // with sigaction to force it.
      if (-1 == ACE_OS::sleep (tmo) && errno == EINTR)
        continue;
      // Timed out
      pid = 0;
      break;
    }

  // Restore the previous SIGCHLD action if it was changed.
  old_action.register_action (SIGCHLD);

  return pid;
#endif /* ACE_WIN32 */
}

void
ACE_Process::close_dup_handles (void)
{
  if (this->dup_handles_.num_set () > 0)
    {
      ACE_Handle_Set_Iterator h_iter (this->dup_handles_);
      for (ACE_HANDLE h = h_iter ();
           h != ACE_INVALID_HANDLE;
           h = h_iter ())
        ACE_OS::closesocket (h);
      this->dup_handles_.reset ();
    }
  return;
}

void
ACE_Process::close_passed_handles (void)
{
  if (this->handles_passed_.num_set () > 0)
    {
      ACE_Handle_Set_Iterator h_iter (this->handles_passed_);
      for (ACE_HANDLE h = h_iter ();
           h != ACE_INVALID_HANDLE;
           h = h_iter ())
        ACE_OS::closesocket (h);
      this->handles_passed_.reset ();
    }
  return;
}

#if defined (ACE_WIN32) && \
    defined (ACE_HAS_WCHAR) && !defined (ACE_USES_WCHAR) && \
    !defined (ACE_HAS_WINCE)
wchar_t*
ACE_Process::convert_env_buffer (const char* env) const
{
  // Total starts out at 1 due to the final block nul terminator
  size_t total = 1;

  // Convert each individual character string to the equivalent wide
  // character string.
  ACE_Vector<wchar_t*> buffer;
  size_t start = 0;
  size_t i = 0;
  while (true)
    {
      if (env[i] == '\0')
        {
          // Convert the char string to wchar_t
          wchar_t* str = ACE_Ascii_To_Wide::convert (env + start);

          // Add the length of the string plus the nul terminator
          total += ACE_OS::strlen (str) + 1;

          // Save it and set up for the next string
          buffer.push_back (str);
          start = ++i;
          if (env[start] == '\0')
            break;
        }
      else
        {
          i += ACE_OS::strlen (env + i);
        }
    }

  // Copy each string into the buffer leaving a nul terminator between
  // each string and adding a second nul terminator at the end
  start = 0;
  wchar_t* wenv = new wchar_t[total];
  size_t length = buffer.size ();
  for (i = 0; i < length; ++i)
    {
      ACE_OS::strcpy(wenv + start, buffer[i]);
      start += ACE_OS::strlen (buffer[i]) + 1;
      delete [] buffer[i];
    }
  wenv[start] = 0;
  return wenv;
}
#endif

ACE_Process_Options::ACE_Process_Options (bool inherit_environment,
                                          size_t command_line_buf_len,
                                          size_t env_buf_len,
                                          size_t max_env_args,
                                          size_t max_cmdline_args)
  :
#if !defined (ACE_HAS_WINCE)
    inherit_environment_ (inherit_environment),
#endif /* ACE_HAS_WINCE */
    creation_flags_ (0),
    avoid_zombies_ (0),
#if !defined (ACE_HAS_WINCE)
#if defined (ACE_WIN32)
    environment_inherited_ (0),
    process_attributes_ (0),
    thread_attributes_ (0),
#else /* ACE_WIN32 */
    stdin_ (ACE_INVALID_HANDLE),
    stdout_ (ACE_INVALID_HANDLE),
    stderr_ (ACE_INVALID_HANDLE),
    ruid_ ((uid_t) -1),
    euid_ ((uid_t) -1),
    rgid_ ((uid_t) -1),
    egid_ ((uid_t) -1),
#endif /* ACE_WIN32 */
    handle_inheritance_ (true),
    set_handles_called_ (0),
    environment_buf_index_ (0),
    environment_argv_index_ (0),
    environment_buf_ (0),
    environment_buf_len_ (env_buf_len),
    max_environment_args_ (max_env_args),
    max_environ_argv_index_ (max_env_args - 1),
#endif /* !ACE_HAS_WINCE */
    command_line_argv_calculated_ (false),
    command_line_buf_ (0),
    command_line_copy_ (0),
    command_line_buf_len_ (command_line_buf_len),
    max_command_line_args_ (max_cmdline_args),
    command_line_argv_ (0),
    process_group_ (ACE_INVALID_PID),
    use_unicode_environment_ (false)
{
  ACE_NEW (command_line_buf_,
           ACE_TCHAR[command_line_buf_len]);
  command_line_buf_[0] = '\0';
  process_name_[0] = '\0';

#if defined (ACE_HAS_WINCE)
  ACE_UNUSED_ARG(inherit_environment);
  ACE_UNUSED_ARG(env_buf_len);
  ACE_UNUSED_ARG(max_env_args);
#endif

#if !defined (ACE_HAS_WINCE)
  working_directory_[0] = '\0';
  ACE_NEW (environment_buf_,
           ACE_TCHAR[env_buf_len]);
  ACE_NEW (environment_argv_,
           ACE_TCHAR *[max_env_args]);
  environment_buf_[0] = '\0';
  environment_argv_[0] = 0;
#if defined (ACE_WIN32)
  ACE_OS::memset ((void *) &this->startup_info_,
                  0,
                  sizeof this->startup_info_);
  this->startup_info_.cb = sizeof this->startup_info_;
#endif /* ACE_WIN32 */
#endif /* !ACE_HAS_WINCE */
  ACE_NEW (command_line_argv_,
           ACE_TCHAR *[max_cmdline_args]);
}

#if !defined (ACE_HAS_WINCE)
#if defined (ACE_WIN32)
void
ACE_Process_Options::inherit_environment (void)
{
  // Ensure only once execution.
  if (environment_inherited_)
    return;
  environment_inherited_ = 1;

  // Get the existing environment.
  ACE_TCHAR *existing_environment = 0;
#if defined (ACE_HAS_WCHAR) && !defined (ACE_USES_WCHAR)
  WCHAR *existing_wide_env = 0;
  ACE_Vector<char> temp_narrow_env;
  if (this->use_unicode_environment_)
    {
      existing_wide_env = ::GetEnvironmentStringsW ();
      for (WCHAR *iter = existing_wide_env; *iter; ++iter)
        {
          ACE_Wide_To_Ascii wta (iter);
          size_t len = ACE_OS::strlen (wta.char_rep ());
          size_t idx = temp_narrow_env.size ();
          temp_narrow_env.resize (idx + len + 1, 0);
          ACE_OS::strncpy (&temp_narrow_env[idx], wta.char_rep (), len);
          iter += len;
        }
      temp_narrow_env.push_back (0);
      existing_environment = &temp_narrow_env[0];
    }
  else
#endif
  existing_environment = ACE_OS::getenvstrings ();

  size_t slot = 0;

  while (existing_environment[slot] != '\0')
    {
      size_t len = ACE_OS::strlen (existing_environment + slot);

      // Add the string to our env buffer.
      if (this->setenv_i (existing_environment + slot, len) == -1)
        {
          ACELIB_ERROR ((LM_ERROR,
                      ACE_TEXT ("%p.\n"),
                      ACE_TEXT ("ACE_Process_Options::ACE_Process_Options")));
          break;
        }

      // Skip to the next word.
      slot += len + 1;
    }

#if defined (ACE_HAS_WCHAR) && !defined (ACE_USES_WCHAR)
  if (this->use_unicode_environment_)
    ::FreeEnvironmentStringsW (existing_wide_env);
  else
#endif
  ACE_TEXT_FreeEnvironmentStrings (existing_environment);
}

#else /* defined ACE_WIN32 */

ACE_TCHAR * const *
ACE_Process_Options::env_argv (void)
{
  return environment_argv_;
}

#endif /* ACE_WIN32 */

int
ACE_Process_Options::setenv (ACE_TCHAR *envp[])
{
  int i = 0;
  while (envp[i])
    {
      if (this->setenv_i (envp[i],
                          ACE_OS::strlen (envp[i])) == -1)
        return -1;
      i++;
    }

#if defined (ACE_WIN32)
  if (inherit_environment_)
    this->inherit_environment ();
#endif /* ACE_WIN32 */

  return 0;
}

int
ACE_Process_Options::setenv (const ACE_TCHAR *format, ...)
{
  ACE_TCHAR stack_buf[DEFAULT_COMMAND_LINE_BUF_LEN];

  // Start varargs.
  va_list argp;
  va_start (argp, format);

  // Add the rest of the varargs.
  ACE_OS::vsprintf (stack_buf,
                    format,
                    argp);
  // End varargs.
  va_end (argp);

  // Append the string to are environment buffer.
  if (this->setenv_i (stack_buf,
                      ACE_OS::strlen (stack_buf)) == -1)
    return -1;

#if defined (ACE_WIN32)
  if (inherit_environment_)
    this->inherit_environment ();
#endif /* ACE_WIN32 */

  return 0;
}

int
ACE_Process_Options::setenv (const ACE_TCHAR *variable_name,
                             const ACE_TCHAR *format, ...)
{
  // To address the potential buffer overflow,
  // we now allocate the buffer on heap with a variable size.
  size_t const buflen = ACE_OS::strlen (variable_name) + ACE_OS::strlen (format) + 2;
  ACE_TCHAR *newformat = 0;
  ACE_NEW_RETURN (newformat, ACE_TCHAR[buflen], -1);
  ACE_Auto_Basic_Array_Ptr<ACE_TCHAR> safe_newformat (newformat);

  // Add in the variable name.
  ACE_OS::sprintf (safe_newformat.get (),
                   ACE_TEXT ("%s=%s"),
                   variable_name,
                   format);

  // Start varargs.
  va_list argp;
  va_start (argp, format);

  // Add the rest of the varargs.
  size_t tmp_buflen = buflen;
  if (DEFAULT_COMMAND_LINE_BUF_LEN > buflen)
    {
      tmp_buflen = DEFAULT_COMMAND_LINE_BUF_LEN;
    }
  int retval = 0;

  ACE_TCHAR *stack_buf = 0;
  ACE_NEW_RETURN (stack_buf, ACE_TCHAR[tmp_buflen], -1);
  ACE_Auto_Basic_Array_Ptr<ACE_TCHAR> safe_stack_buf (stack_buf);

  do
    {
      retval = ACE_OS::vsnprintf (safe_stack_buf.get (), tmp_buflen, safe_newformat.get (), argp);
      if (retval > ACE_Utils::truncate_cast<int> (tmp_buflen))
        {
          tmp_buflen *= 2;
          ACE_NEW_RETURN (stack_buf, ACE_TCHAR[tmp_buflen], -1);
          safe_stack_buf.reset (stack_buf);
        }
      else
        break;
    }
  while (1);

  if (retval == -1)
    {
      // In case that vsnprintf is not supported,
      // e.g., LynxOS and VxWorks 5, we have to
      // fall back to vsprintf.
      if (errno == ENOTSUP)
        {
          // ALERT: Since we have to use vsprintf here, there is still a chance that
          // the stack_buf overflows, i.e., the length of the resulting string
          // can still possibly go beyond the allocated stack_buf.
          retval = ACE_OS::vsprintf (safe_stack_buf.get (), safe_newformat.get (), argp);
          if (retval == -1)
            // vsprintf is failed.
            return -1;
        }
      else
        // vsnprintf is failed.
        return -1;
    }

  // End varargs.
  va_end (argp);

  // Append the string to our environment buffer.
  if (this->setenv_i (safe_stack_buf.get (),
                      ACE_OS::strlen (safe_stack_buf.get ())) == -1)
    return -1;

#if defined (ACE_WIN32)
  if (inherit_environment_)
    this->inherit_environment ();
#endif /* ACE_WIN32 */

  return 0;
}

int
ACE_Process_Options::setenv_i (ACE_TCHAR *assignment,
                               size_t len)
{
  // Add one for the null char.
  ++len;

  // If environment larger than allocated buffer return. Also check to
  // make sure we have enough room.
  if (environment_argv_index_ == max_environ_argv_index_
      || (len + environment_buf_index_) >= environment_buf_len_)
    return -1;

  // Copy the new environment string.
  ACE_OS::memcpy (environment_buf_ + environment_buf_index_,
                  assignment,
                  len * sizeof (ACE_TCHAR));

  // Update the argv array.
  environment_argv_[environment_argv_index_++] =
    environment_buf_ + environment_buf_index_;
  environment_argv_[environment_argv_index_] = 0;

  // Update our index.
  environment_buf_index_ += len;

  // Make sure the buffer is null-terminated.
  environment_buf_[environment_buf_index_] = '\0';
  return 0;
}

int
ACE_Process_Options::set_handles (ACE_HANDLE std_in,
                                  ACE_HANDLE std_out,
                                  ACE_HANDLE std_err)
{
  this->set_handles_called_ = 1;
#if defined (ACE_WIN32)

  // Tell the new process to use our std handles.
  this->startup_info_.dwFlags = STARTF_USESTDHANDLES;

  if (std_in == ACE_INVALID_HANDLE)
    std_in = ACE_STDIN;
  if (std_out == ACE_INVALID_HANDLE)
    std_out = ACE_STDOUT;
  if (std_err == ACE_INVALID_HANDLE)
    std_err = ACE_STDERR;

  if (!::DuplicateHandle (::GetCurrentProcess (),
                          std_in,
                          ::GetCurrentProcess (),
                          &this->startup_info_.hStdInput,
                          0,
                          TRUE,
                          DUPLICATE_SAME_ACCESS))
    return -1;

  if (!::DuplicateHandle (::GetCurrentProcess (),
                          std_out,
                          ::GetCurrentProcess (),
                          &this->startup_info_.hStdOutput,
                          0,
                          TRUE,
                          DUPLICATE_SAME_ACCESS))
    return -1;

  if (!::DuplicateHandle (::GetCurrentProcess (),
                          std_err,
                          ::GetCurrentProcess (),
                          &this->startup_info_.hStdError,
                          0,
                          TRUE,
                          DUPLICATE_SAME_ACCESS))
    return -1;
#else /* ACE_WIN32 */
  this->stdin_ = ACE_OS::dup (std_in);
  this->stdout_ = ACE_OS::dup (std_out);
  this->stderr_ = ACE_OS::dup (std_err);
#endif /* ACE_WIN32 */

  return 0; // Success.
}


void
ACE_Process_Options::release_handles ()
{
  if (set_handles_called_)
    {
#if defined (ACE_WIN32)
      ACE_OS::close (startup_info_.hStdInput);
      ACE_OS::close (startup_info_.hStdOutput);
      ACE_OS::close (startup_info_.hStdError);
#else /* ACE_WIN32 */
      ACE_OS::close (stdin_);
      ACE_OS::close (stdout_);
      ACE_OS::close (stderr_);
#endif /* ACE_WIN32 */
      set_handles_called_ = 0;
    }
}
#endif /* !ACE_HAS_WINCE */


ACE_Process_Options::~ACE_Process_Options (void)
{
#if !defined (ACE_HAS_WINCE)
  release_handles();
  delete [] environment_buf_;
  delete [] environment_argv_;
#endif /* !ACE_HAS_WINCE */
  delete [] command_line_buf_;
  ACE::strdelete (command_line_copy_);
  delete [] command_line_argv_;
}

int
ACE_Process_Options::command_line (const ACE_TCHAR *const argv[])
{
  int i = 0;

  if (argv[i])
    {
      ACE_OS::strcat (command_line_buf_, argv[i]);

      while (argv[++i])
        {
          // Check to see if the next argument will overflow the
          // command_line buffer.
          size_t const cur_len =
            ACE_OS::strlen (command_line_buf_)
              + ACE_OS::strlen (argv[i])
              + 2;

          if (cur_len > command_line_buf_len_)
            {
              ACELIB_ERROR_RETURN ((LM_ERROR,
                                 ACE_TEXT ("ACE_Process:command_line: ")
                                 ACE_TEXT ("command line is ")
                                 ACE_TEXT ("longer than %d\n"),
                                 command_line_buf_len_),
                                1);
            }

          ACE_OS::strcat (command_line_buf_, ACE_TEXT (" "));
          ACE_OS::strcat (command_line_buf_, argv[i]);
        }
    }

  command_line_argv_calculated_ = false;
  return 0; // Success.
}

int
ACE_Process_Options::command_line (const ACE_TCHAR *format, ...)
{
  // Store all ... args in argp.
  va_list argp;
  va_start (argp, format);

  if (command_line_buf_len_ < 1)
    {
      va_end (argp);
      return -1;
    }

#if !defined (ACE_LACKS_VSNPRINTF) || defined (ACE_HAS_TRIO)
  // vsnprintf the format and args into command_line_buf__.
  ACE_OS::vsnprintf (command_line_buf_,
                     command_line_buf_len_,
                     format,
                     argp);
#else
  // sprintf the format and args into command_line_buf__.
  ACE_OS::vsprintf (command_line_buf_,
                    format,
                    argp);
#endif

  // Useless macro.
  va_end (argp);

  command_line_argv_calculated_ = false;
  return 0;
}

#if defined (ACE_HAS_WCHAR) && !defined (ACE_HAS_WINCE)
/**
 * @note Not available on Windows CE because it doesn't have a char version of
 * vsprintf.
 */
int
ACE_Process_Options::command_line (const ACE_ANTI_TCHAR *format, ...)
{
  ACE_ANTI_TCHAR *anti_clb = 0;
  ACE_NEW_RETURN (anti_clb,
                  ACE_ANTI_TCHAR[this->command_line_buf_len_],
                  -1);

  // Store all ... args in argp.
  va_list argp;
  va_start (argp, format);

  // sprintf the format and args into command_line_buf_.
  ACE_OS::vsprintf (anti_clb,
                    format,
                    argp);

  // Useless macro.
  va_end (argp);

  ACE_OS::strcpy (this->command_line_buf_,
                  ACE_TEXT_ANTI_TO_TCHAR (anti_clb));

  delete [] anti_clb;

  command_line_argv_calculated_ = false;
  return 0;
}
#endif /* ACE_HAS_WCHAR && !ACE_HAS_WINCE */

ACE_TCHAR *
ACE_Process_Options::env_buf (void)
{
#if !defined (ACE_HAS_WINCE)
  if (environment_buf_[0] == '\0')
    return 0;
  else
    return environment_buf_;
#else
  return 0;
#endif /* !ACE_HAS_WINCE */
}

ACE_TCHAR * const *
ACE_Process_Options::command_line_argv (void)
{
  if (!command_line_argv_calculated_)
    {
      command_line_argv_calculated_ = true;

      // We need to free up any previous allocated memory first.
      ACE::strdelete (command_line_copy_);

      // We need to make a dynamically allocated copy here since
      // ACE_Tokenizer modifies its arguments.
      command_line_copy_ = ACE::strnew (command_line_buf_);
      // This tokenizer will replace all spaces with end-of-string
      // characters and will preserve text between "" and '' pairs.
      ACE_Tokenizer parser (command_line_copy_);
      parser.delimiter_replace (' ', '\0');
      parser.preserve_designators ('\"', '\"'); // "
      parser.preserve_designators ('\'', '\'');

      unsigned int x = 0;
      do
        command_line_argv_[x] = parser.next ();
      while (command_line_argv_[x] != 0
             // subtract one for the ending zero.
             && ++x < max_command_line_args_ - 1);

      command_line_argv_[x] = 0;
    }

  return command_line_argv_;
}

// Cause the specified handle to be passed to a child process
// when it's spawned.
int
ACE_Process_Options::pass_handle (ACE_HANDLE h)
{
# if defined (ACE_WIN32)
#  if defined (ACE_HAS_WINCE)
  ACE_NOTSUP_RETURN (-1);
#  else

  // This is oriented towards socket handles... may need some adjustment
  // for non-sockets.
  // This is all based on an MSDN article:
  // http://support.microsoft.com/support/kb/articles/Q150/5/23.asp
  // If on Win95/98, the handle needs to be duplicated for the to-be-spawned
  // process. On WinNT, they get inherited by the child process automatically.
  // If the handle is duplicated, remember the duplicate so it can be
  // closed later. Can't be closed now, or the child won't get it.
  ACE_TEXT_OSVERSIONINFO osvi;
  ZeroMemory (&osvi, sizeof (osvi));
  osvi.dwOSVersionInfoSize = sizeof (ACE_TEXT_OSVERSIONINFO);
  // If this is Win95/98 or we can't tell, duplicate the handle.
  if (!ACE_TEXT_GetVersionEx (&osvi) || osvi.dwPlatformId != VER_PLATFORM_WIN32_NT)
    {
      HANDLE dup_handle;
      if (!DuplicateHandle (GetCurrentProcess (),
                            static_cast<HANDLE> (h),
                            GetCurrentProcess (),
                            &dup_handle,
                            0,
                            TRUE,   // Inheritable
                            DUPLICATE_SAME_ACCESS))
        return -1;
      dup_handles_.set_bit (static_cast<ACE_HANDLE> (dup_handle));
    }
#  endif /* ACE_HAS_WINCE */
#endif /* ACE_WIN32 */

  this->handles_passed_.set_bit (h);

  return 0;
}

// Get a copy of the handles the ACE_Process_Options duplicated
// for the spawned process.
int
ACE_Process_Options::dup_handles (ACE_Handle_Set &set) const
{
  if (this->dup_handles_.num_set () == 0)
    return 0;
  set.reset ();
  set = this->dup_handles_;
  return 1;
}

// Get a copy of the handles passed to the spawned process. This
// will be the set of handles previously passed to @arg pass_handle().
int
ACE_Process_Options::passed_handles (ACE_Handle_Set &set) const
{
  if (this->handles_passed_.num_set () == 0)
    return 0;
  set.reset ();
  set = this->handles_passed_;
  return 1;
}

ACE_Managed_Process::~ACE_Managed_Process (void)
{
}

void
ACE_Managed_Process::unmanage (void)
{
  delete this;
}

ACE_END_VERSIONED_NAMESPACE_DECL
