// -*- C++ -*-

//=============================================================================
/**
 *  @file    Process.h
 *
 *  @author Tim Harrison <harrison@cs.wustl.edu>
 */
//=============================================================================

#ifndef ACE_PROCESS_H
#define ACE_PROCESS_H

#include /**/ "ace/pre.h"

#include /**/ "ace/ACE_export.h"

#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */

#include "ace/Handle_Set.h"
#include "ace/Global_Macros.h"
#include "ace/os_include/sys/os_types.h"

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

// Forward declaration
class ACE_Time_Value;

/**
 * @class ACE_Process_Options
 *
 * @brief Process Options
 *
 * This class controls the options passed to <CreateProcess> (or <fork>
 * and <exec>).
 * Notice that on Windows CE, creating a process merely means
 * instantiating a new process.  You can't set the handles (since
 * there's no stdin, stdout and stderr,) specify process/thread
 * options, set environment,...  So, basically, this class only
 * set the command line and nothing else.
 * Notice that on UNIX platforms, if the <setenv> is used, the
 * <spawn> is using the <execve> system call. It means that the
 * <command_line> should include a full path to the program file
 * (<execve> does not search the PATH).  If <setenv> is not used
 * then, the <spawn> is using the <execvp> which searches for the
 * program file in the PATH variable.
 */
class ACE_Export ACE_Process_Options
{
public:
  enum
  {
    DEFAULT_COMMAND_LINE_BUF_LEN = 1024,
    // UNIX process creation flags.
#if defined (ACE_WIN32)
    NO_EXEC = 0
#else
    NO_EXEC = 1
#endif /* ACE_WIN32 */
  };

protected:
  // = Default settings not part of public Interface.
  //
  /// @todo These sizes should be taken from the appropriate
  /// POSIX/system header files and/or defined dynamically.
  enum
  {
    MAX_COMMAND_LINE_OPTIONS = 128,
    ENVIRONMENT_BUFFER = 16 * 1024, // 16K
    MAX_ENVIRONMENT_ARGS = 512 //
  };

public:
  /**
   * If @a inherit_environment == true, the new process will inherit the
   * environment of the current process.  @a command_line_buf_len is the
   * max strlen for command-line arguments.
   */
  ACE_Process_Options (bool inherit_environment = true,
                       size_t command_line_buf_len = DEFAULT_COMMAND_LINE_BUF_LEN,
                       size_t env_buf_len = ENVIRONMENT_BUFFER,
                       size_t max_env_args = MAX_ENVIRONMENT_ARGS,
                       size_t max_cmdline_args = MAX_COMMAND_LINE_OPTIONS);

  /// Destructor.
  ~ACE_Process_Options ();

  // = Methods to set process creation options portably.

  /**
   * Set the standard handles of the new process to the respective
   * handles.  If you want to affect a subset of the handles, make
   * sure to set the others to ACE_INVALID_HANDLE.
   *
   * @note Any handle passed as ACE_INVALID_HANDLE will be changed to
   * a duplicate of the current associated handle. For example, passing
   * ACE_INVALID_HANDLE for @a std_in will cause ACE_STDIN to be
   * duplicated and set in this object.
   *
   * @note Windows: The implementation of set_handles() uses DuplicateHandle
   *       on Windows. DuplicateHandle cannot be used to pass a socket handle
   *       on Windows. Socket handles require an alternate mechanism to pass;
   *       see http://msdn.microsoft.com/en-us/library/ms741565(v=VS.85).aspx
   *
   * @return 0 on success, -1 on failure.
   */
  int set_handles (ACE_HANDLE std_in,
                   ACE_HANDLE std_out = ACE_INVALID_HANDLE,
                   ACE_HANDLE std_err = ACE_INVALID_HANDLE);

  /// Release the standard handles previously set with set_handles;
  void release_handles ();

#ifndef ACE_LACKS_VA_FUNCTIONS
  /// @param format must be of the form "VARIABLE=VALUE".  There can not be
  /// any spaces between VARIABLE and the equal sign.
  int setenv (const ACE_TCHAR *format,
              ...)
#if !defined (ACE_USES_WCHAR)
    ACE_GCC_FORMAT_ATTRIBUTE (printf, 2, 3)
#endif /* !ACE_USES_WCHAR */
    ;

  /**
   * Set a single environment variable, @a variable_name.  Since
   * different platforms separate each environment variable
   * differently, you must call this method once for each variable.
   * @a format can be any printf format string.  So options->setenv
   * ("FOO","one + two = %s", "three") will result in "FOO=one + two =
   * three".
   */
  int setenv (const ACE_TCHAR *variable_name,
              const ACE_TCHAR *format,
              ...)
#if !defined (ACE_USES_WCHAR)
    ACE_GCC_FORMAT_ATTRIBUTE (printf, 3, 4)
#endif /* !ACE_USES_WCHAR */
    ;
#endif // ACE_LACKS_VA_FUNCTIONS

  /// Same as above with argv format.  @a envp must be null terminated.
  int setenv (ACE_TCHAR *envp[]);

  /// Set the working directory for the process.  strlen of @a wd must
  /// be <= MAXPATHLEN.
  void working_directory (const char *wd);

#if defined (ACE_HAS_WCHAR)
  /// wchar_t version of working_directory
  void working_directory (const wchar_t *wd);
#endif /* ACE_HAS_WCHAR */

#ifndef ACE_LACKS_VA_FUNCTIONS
  /**
   * Set the command-line arguments.  @a format can use any printf
   * formats.  The first token in @a format should be the path to the
   * application.  This can either be a full path, relative path, or
   * just an executable name.  If an executable name is used, we rely
   * on the platform's support for searching paths.  Since we need a
   * path to run a process, this method *must* be called!  Returns 0
   * on success, -1 on failure.
   */
  int command_line (const ACE_TCHAR *format, ...)
#if !defined (ACE_USES_WCHAR)
    ACE_GCC_FORMAT_ATTRIBUTE (printf, 2, 3)
#endif /* !ACE_USES_WCHAR */
    ;

#if defined (ACE_HAS_WCHAR) && !defined (ACE_HAS_WINCE)
  /// Anti-TChar version of command_line ()
  int command_line (const ACE_ANTI_TCHAR *format, ...);
#endif /* ACE_HAS_WCHAR && !ACE_HAS_WINCE */
#endif // ACE_LACKS_VA_FUNCTIONS

  /// Same as above in argv format.  @a argv must be null terminated.
  int command_line (const ACE_TCHAR * const argv[]);

  /**
   * Specify the full path or relative path, or just the executable
   * name for the process. If this is set, then @a name will be used to
   * create the process instead of argv[0] set in the command
   * line. This is here so that you can supply something other than
   * executable name as argv[0].
   */
  void process_name (const ACE_TCHAR *name);

  /// Return the process_name.  If the <process_name(name)> set
  /// method is not called, this method will return argv[0].
  const ACE_TCHAR *process_name ();

  /// Get the creation flags.
  u_long creation_flags () const;

  /**
   * Set the creation flags to affect how a new process is spawned.
   * The only ACE-defined flag is @c NO_EXEC which prevents the new process
   * from executing a new program image; this is a simple POSIX fork().
   * The @c NO_EXEC option has no affect on Windows; on other platforms where
   * a POSIX fork is not possible, specifying @c NO_EXEC will cause
   * ACE_Process::spawn() to fail.
   *
   * On Windows, the value of creation_flags is passed to the @c CreateProcess
   * system call as the value of the @c dwCreationFlags parameter.
   */
  void creation_flags (u_long);

  /// Current working directory.  Returns "" if nothing has been set.
  ACE_TCHAR *working_directory ();

  /// Buffer of command-line options.  Returns a pointer to a buffer that
  /// contains the list of command line options.  Prior to a call to
  /// command_line_argv(), this is a single string of space separated
  /// arguments independent of which form of command_line() was used to
  /// create it.  After a call to command_line_argv(), this is a list of
  /// strings each terminated by '\0'.  [Note: spawn() will call
  /// command_line_argv().]  The total length of all these strings is the
  /// same as the single string in the prior case and can be obtained by
  /// providing max_len. @arg max_len, if non-zero, provides a location
  /// into which the total length of the command line buffer is returned.
  ACE_TCHAR *command_line_buf (size_t *max_len = 0);

  /**
   * argv-style command-line options.  Parses and modifies the string
   * created from <command_line_>.  All spaces not in quotes ("" or
   * '') are replaced with null (\0) bytes.  An argv array is built
   * and returned with each entry pointing to the start of
   * null-terminated string.  Returns { 0 } if nothing has been set.
   */
  ACE_TCHAR * const *command_line_argv ();

  /**
   * Null-terminated buffer of null terminated strings.  Each string
   * is an environment assignment "VARIABLE=value".  This buffer
   * should end with two null characters.
   */
  ACE_TCHAR *env_buf ();

  /// Get the process group.  On UNIX, these methods are used by the
  /// ACE_Process_Manager to manage groups of processes.
  pid_t getgroup () const;

  /// Set the process group.  On UNIX, these methods are used by the
  /// ACE_Process_Manager to manage groups of processes.
  pid_t setgroup (pid_t pgrp);

  /// Allows disabling of handle inheritance, default is TRUE.
  ///
  /// @remarks @b Windows: the handle_inheritance value is passed as the
  /// bInheritHandles value to the CreateProcess() system function. Therefore,
  /// if you redirect standard input, output, or error via
  /// ACE_Process_Options::set_handles() you must not call
  /// handle_inheritance(false). Doing so will prevent the duplicated handles
  /// from surviving in the created process.
  int handle_inheritance ();
  void handle_inheritance (int);

  /// Cause the specified handle to be passed to a child process
  /// when it runs a new program image.
  /**
   * The specified handle value will be included in the spawned
   * process's command line as @arg +H @arg handle, if a new
   * program is spawned (always on Win32; else if NO_EXEC is not
   * set in creation flags).  The passed handle value will be
   * duplicated if on Win32 less capable than NT.
   * @return 0 if success, -1 if failure.
   */
  int pass_handle (ACE_HANDLE);

  /// Get a copy of the handles the ACE_Process_Options duplicated
  /// for the spawned process.
  /**
   * Any handles created through duplication of those passed into
   * @arg pass_handle are returned in @arg set.
   * @return 0 if there were no handles to return; 1 if there were.
   */
  int dup_handles (ACE_Handle_Set &set) const;

  /// Get a copy of the handles passed to the spawned process. This
  /// will be the set of handles previously passed to @arg pass_handle().
  /**
   * Any handles previously passed to @arg pass_handle are returned
   * in @arg set.
   * @return 0 if there were no handles to return; 1 if there were.
   */
  int passed_handles (ACE_Handle_Set &set) const;

  /// Set value for avoid_zombies (has no real effect except on *nix).
  void avoid_zombies (int);

  /// Get current value for avoid_zombies.
  int avoid_zombies ();

  /// Enable the use of a Unicode environment.  This only makes sense
  /// for Win32 when ACE_USES_WCHAR is not defined.
  void enable_unicode_environment ();

  /// Disable the use of a Unicode environment.
  void disable_unicode_environment ();

  /// Return the unicode environment status
  bool use_unicode_environment () const;

#if defined (ACE_WIN32)
  // = Non-portable accessors for when you "just have to use them."

  /// Used for setting and getting.
  ACE_TEXT_STARTUPINFO *startup_info (void);

  /// Get the process_attributes.  Returns NULL if
  /// set_process_attributes has not been set.
  LPSECURITY_ATTRIBUTES get_process_attributes (void) const;

  /// If this is called, a non-null process attributes is sent to
  /// CreateProcess.
  LPSECURITY_ATTRIBUTES set_process_attributes (void);

  /// Get the thread_attributes.  Returns NULL if set_thread_attributes
  /// has not been set.
  LPSECURITY_ATTRIBUTES get_thread_attributes (void) const;

  /// If this is called, a non-null thread attributes is sent to
  /// CreateProcess.
  LPSECURITY_ATTRIBUTES set_thread_attributes (void);

  /// Get user token. Return ACE_INVALID_HANDLE if it has not been set.
  HANDLE get_user_token (void) const;

  /// Set user token for creating process as user.
  /// @param token the user token is passed to \c ::CreateProcessAsUser.
  /// @param close_token whether to close the user token when destructing.
  void set_user_token (HANDLE token, bool close_token = false);

#else /* All things not WIN32 */

  /// argv-style array of environment settings.
  ACE_TCHAR *const *env_argv ();

  // = Accessors for the standard handles.
  ACE_HANDLE get_stdin () const;
  ACE_HANDLE get_stdout () const;
  ACE_HANDLE get_stderr () const;

  // = Set/get real & effective user & group id associated with user.
  int setreugid (const ACE_TCHAR* user);
  void setruid (uid_t id);
  void seteuid (uid_t id);
  void setrgid (uid_t id);
  void setegid (uid_t id);
  uid_t getruid () const;
  uid_t geteuid () const;
  uid_t getrgid () const;
  uid_t getegid () const;

  /**
   * Get the inherit_environment flag.
   */
  bool inherit_environment () const;

  /**
   * Set the inherit_environment flag.
   */
  void inherit_environment (bool nv);
#endif /* ACE_WIN32 */
protected:

#if !defined (ACE_HAS_WINCE)
  /// Add @a assignment to environment_buf_ and adjust
  /// environment_argv_.  @a len is the strlen of @a assignment.
  int setenv_i (ACE_TCHAR *assignment, size_t len);

  /// Whether the child process inherits the current process
  /// environment.
  bool inherit_environment_;
#endif /* !ACE_HAS_WINCE */

  /// Default 0.
  u_long creation_flags_;

  /// Avoid zombies for spawned processes.
  int avoid_zombies_;

#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)
  /// Helper function to grab win32 environment and stick it in
  /// environment_buf_ using this->setenv_i.
  void inherit_environment (void);

  /// Ensures once only call to inherit environment.
  int environment_inherited_;

  ACE_TEXT_STARTUPINFO startup_info_;

  /// Pointer to security_buf1_.
  LPSECURITY_ATTRIBUTES process_attributes_;

  /// Pointer to security_buf2_.
  LPSECURITY_ATTRIBUTES thread_attributes_;

  /// Data for process_attributes_.
  SECURITY_ATTRIBUTES security_buf1_;

  /// Data for thread_attributes_.
  SECURITY_ATTRIBUTES security_buf2_;

  /// User token for \a CreateProcessAsUser
  HANDLE user_token_;

  /// Keeps track of whether we need to close user token.
  bool close_user_token_;
#else /* !ACE_WIN32 */
  ACE_HANDLE stdin_;
  ACE_HANDLE stdout_;
  ACE_HANDLE stderr_;

  // = Real & effective user & group id's.
  //   These should be set to -1 to leave unchanged (default).
  uid_t ruid_;
  uid_t euid_;
  uid_t rgid_;
  uid_t egid_;
#endif /* ACE_WIN32 */

  /// Default true.
  bool handle_inheritance_;

#if !defined (ACE_HAS_WINCE)
  /// Is 1 if stdhandles was called.
  int set_handles_called_;

  /// Pointer into environment_buf_.  This should point to the next
  /// free spot.
  size_t environment_buf_index_;

  /// Pointer to environment_argv_.
  size_t environment_argv_index_;

  /// Pointer to buffer of the environment settings.
  ACE_TCHAR *environment_buf_;

  /// Size of the environment buffer. Configurable
  size_t environment_buf_len_;

  /// Pointers into environment_buf_.
  ACE_TCHAR **environment_argv_;

  /// Maximum number of environment variables. Configurable
  size_t max_environment_args_;

  /// Maximum index of environment_argv_ buffer
  size_t max_environ_argv_index_;

  /// The current working directory.
  ACE_TCHAR working_directory_[MAXPATHLEN + 1];
#endif /* !ACE_HAS_WINCE */

  /// Ensures command_line_argv is only calculated once.
  bool command_line_argv_calculated_;

  /// Pointer to buffer of command-line arguments.  E.g., "-f foo -b bar".
  ACE_TCHAR *command_line_buf_;

  /// Pointer to copy of command-line arguments, which is needed when
  /// converting a command-line string into a command-line argv.
  ACE_TCHAR *command_line_copy_;

  /// Max length of command_line_buf_
  size_t command_line_buf_len_;

  /// Maximum number of command-line arguments. Configurable
  size_t max_command_line_args_;

  /// Argv-style command-line arguments.
  ACE_TCHAR **command_line_argv_;

  /// Process-group on Unix; unused on Win32.
  pid_t process_group_;

  /// Set of handles that were passed in pass_handle ().
  ACE_Handle_Set handles_passed_;

  /// Results of duplicating handles passed in pass_handle ().
  ACE_Handle_Set dup_handles_;

  /// Pathname for the process. Relative path or absolute path or just
  /// the program name.
  ACE_TCHAR process_name_[MAXPATHLEN + 1];

  /// Indicate if a Unicode environment should be used
  bool use_unicode_environment_;
};

//class ACE_Process_Manager;

/**
 * @class ACE_Process
 *
 * @brief A portable encapsulation for creating and managing new processes.
 *
 * ACE_Process provides a convenient way to:
 *  - Spawn child processes, with convenient hooks for pre- and post-spawn
 *    actions
 *  - Check if a spawned process is still running
 *  - Kill a spawned child process
 *  - Wait for a spawned child process to exit.
 *
 * @see ACE_Process_Options because it is used to
 * pass options when spawning child processes.
 *
 * @see ACE_Process_Manager for additional ways to manage spawned
 * processes.
 */
class ACE_Export ACE_Process
{
public:
  friend class ACE_Process_Manager;

  /// Default construction.  Use ACE_Process::spawn() to start a process.
  ACE_Process ();

  /// Destructor.
  virtual ~ACE_Process ();

  /**
   * Called back from spawn() just before spawning the child.  If this
   * returns non-zero, the spawn is aborted (and returns ACE_INVALID_PID).
   * The default returns zero.
   */
  virtual int prepare (ACE_Process_Options &options);

  /**
   * Launch a new process as described by @a options.
   *
   * @retval -1 on failure; check @c errno for error code.
   * @retval 1 on success if the option @c avoid_zombies is set.
   * @retval other the process id of the newly spawned child.
   *
   * @note The return value 1 may be changed in future versions of ACE to be
   * the process id of the child will be returned regardless of the
   * @c avoid_zombies option.
   *
   * @note On UNIX platforms, spawn() uses the execvp() system call if
   * ACE_Process_Options::inherit_environment() returns true (which is the
   * default) and execve() if not. Since execve() does not search PATH, the
   * ACE_Process_Options::command_line() should include a full path to the
   * program file.
   */
  virtual pid_t spawn (ACE_Process_Options &options);

  /// Called back from spawn() in the parent's context just after forking,
  /// if the fork succeeds.  The default simply returns.
  virtual void parent (pid_t child);

  /**
   * Called back from spawn() in the child's context just after forking.  The
   * default does nothing.
   *
   * @note This function is *not* called on Windows
   * because the process-creation scheme does not allow it.
   */
  virtual void child (pid_t parent);

  /// Called by a ACE_Process_Manager that is removing this object from
  /// its table of managed processes. Default is to do nothing.
  virtual void unmanage ();

  /**
   * Wait for a previously spawned process to exit.
   *
   * @arg status Points to a location to receive the exit status of the
   *      spawned process. Ignored if the value is 0.
   * @arg wait_options If @c WNOHANG then return 0 and don't block if the
   *      child process hasn't exited yet.
   *
   * @retval -1 the wait operation failed; consult @c errno for details.
   * @retval other the child process id is returned on success.
   */
  pid_t wait (ACE_exitcode *status = 0,
              int wait_options = 0);

  /**
   * Timed wait for a previously spawned process to exit.
   *
   * @arg tv A relative amount of time to wait for the process to exit.
   * @arg status Points to a location to receive the exit status of the
   *      spawned process. Ignored if the value is 0.
   *
   * @retval 0 the specified time period elapsed before the process exited.
   * @retval -1 the wait operation failed; consult @c errno for details.
   * @retval other the child process id is returned on success.
   *
   * @note On UNIX platforms this function uses @c ualarm(), i.e., it
   * overwrites any existing alarm.  In addition, it steals all
   * @c SIGCHLD signals during the timeout period, which will break another
   * ACE_Process_Manager in the same process that's expecting
   * @c SIGCHLD to kick off process reaping.
   */
  pid_t wait (const ACE_Time_Value &tv,
              ACE_exitcode *status = 0);

  /// Send the process a signal.  This only has an effect on operating
  /// systems that support signals, such as UNIX/POSIX.
  int kill (int signum = SIGINT);

  /**
   * Terminate the process abruptly using ACE::terminate_process().
   * This call doesn't give the process a chance to cleanup, so use it
   * with caution.
   */
  int terminate ();

  /// Return the process id of the new child process.
  pid_t getpid () const;

  /// Return the handle of the process, if it has one.
  ACE_HANDLE gethandle () const;

  /// Return 1 if running; 0 otherwise.
  int running () const;

  /// Return the process's exit code.  This method returns the raw
  /// exit status returned from system APIs (such as @c wait() or
  /// @c waitpid() ).  This value is system dependent.
  ACE_exitcode exit_code () const;

  /// Return the process's return value.  This method returns the
  /// actual return value that a child process returns or exits with.
  int return_value () const;

  /// Close all the handles in the set obtained from the
  /// @a ACE_Process_Options::dup_handles object used to spawn
  /// the process.
  void close_dup_handles ();

  /// Close all the passed handles in the set obtained from the
  /// ACE_Process_Options object used to spawn the process.
  void close_passed_handles ();

#if defined (ACE_WIN32)
  PROCESS_INFORMATION process_info (void);
#endif /* ACE_WIN32 */

private:
  ACE_Process (const ACE_Process &) = delete;
  void operator= (const ACE_Process &) = delete;
  ACE_Process (ACE_Process &&) = delete;
  void operator= (ACE_Process &&) = delete;

protected:
  /// Set this process's exit code.  ACE_Process_Manager uses this
  /// method to set the exit code after successfully waiting for
  /// this process to exit.
  void exit_code (ACE_exitcode code);

#if defined (ACE_WIN32)
  PROCESS_INFORMATION process_info_;
#else /* ACE_WIN32 */
  /// Process id of the child.
  pid_t child_id_;
#endif /* ACE_WIN32 */
  ACE_exitcode exit_code_;

  /// Set of handles that were passed to the child process.
  ACE_Handle_Set handles_passed_;

  /// Handle duplicates made for the child process.
  ACE_Handle_Set dup_handles_;

private:
#if defined (ACE_WIN32) && \
    defined (ACE_HAS_WCHAR) && !defined (ACE_USES_WCHAR) && \
    !defined (ACE_HAS_WINCE)
  wchar_t* convert_env_buffer (const char* env) const;
#endif
};

/**
 * @class ACE_Managed_Process
 *
 * @brief A process easily managed by ACE_Process_Manager.
 *
 * @arg ACE_Managed_Process is just an @arg ACE_Process with an
 * @arg unmanage() method that deletes the instance.
 * This class is only valid for use as a dynamically-allocated object!
 */
class ACE_Export ACE_Managed_Process : public ACE_Process
{
public:

  /// Cleanup by deleting @c this.
  virtual void unmanage ();

  ACE_ALLOC_HOOK_DECLARE;

protected:

  /// Make sure that we're allocated dynamically!
  virtual ~ACE_Managed_Process ();
};

ACE_END_VERSIONED_NAMESPACE_DECL

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

#include /**/ "ace/post.h"
#endif /* ACE_PROCESS_H */
