diff options
Diffstat (limited to 'ACE/ace/Process.cpp')
-rw-r--r-- | ACE/ace/Process.cpp | 1408 |
1 files changed, 1408 insertions, 0 deletions
diff --git a/ACE/ace/Process.cpp b/ACE/ace/Process.cpp new file mode 100644 index 00000000000..087d68fb7a2 --- /dev/null +++ b/ACE/ace/Process.cpp @@ -0,0 +1,1408 @@ +// $Id$ + +#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_Msg.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) && (ACE_VXWORKS > 0x600) && defined (__RTP__) +# include <rtpLib.h> +# include <taskLib.h> +#endif + +ACE_RCSID (ace, Process, "$Id$") + +// 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)) + { + int maxlen = 0; + ACE_TCHAR *cmd_line_buf = options.command_line_buf (&maxlen); + size_t max_len = static_cast<size_t> (maxlen); + 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) + curr_len += ACE_OS::sprintf (&cmd_line_buf[curr_len], + ACE_TEXT (" +H %I64p"), + h); +# 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) && (ACE_VXWORKS > 0x600)) && 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 ACE_ERROR(), etc. + // will invoke async signal unsafe functions, which results + // in undefined behavior in threaded programs. + ACE_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 ACE_ERROR(), etc. + // will invoke async signal unsafe functions, which results + // in undefined behavior in threaded programs. + ACE_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 ACE_ERROR(), etc. + // will invoke async signal unsafe functions, which results + // in undefined behavior in threaded programs. + ACE_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 + { +# if defined (ghs) + // GreenHills 1.8.8 (for VxWorks 5.3.x) can't compile this + // code. Processes aren't supported on VxWorks anyways. + ACE_NOTSUP_RETURN (ACE_INVALID_PID); +# else + result = ACE_OS::execve (procname, procargv, procenv); +# endif /* ghs */ + } + 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) + : +#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), + 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 */ +} + +#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) + { + ACE_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_); +} + +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_) + { + ACE_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) + 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 ('\'', '\''); + + int x = 0; + do + command_line_argv_[x] = parser.next (); + while (command_line_argv_[x] != 0 + // substract one for the ending zero. + && ++x < MAX_COMMAND_LINE_OPTIONS - 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 |