#include "ace/Process.h" #if !defined (__ACE_INLINE__) #include "ace/Process.inl" #endif /* __ACE_INLINE__ */ #include "ace/ARGV.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 # include #endif #include // 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 () : #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 () { #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 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_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; if (options.get_user_token () == ACE_INVALID_HANDLE) { 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_); } else { fork_result = ACE_TEXT_CreateProcessAsUser (options.get_user_token (), 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_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 (options.command_line_argv ()); const char **procenv = const_cast (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 // 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 () != (gid_t) -1 || options.getegid () != (gid_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 static_cast(-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 . 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. # ifdef ACE_LACKS_EXECVP result = ACE_OS::execv (procname, procargv); # else result = ACE_OS::execvp (procname, procargv); # endif } 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 () { // nothing to do } int ACE_Process::running () 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 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_Handler_Ex sigchld_nop_ptr = sigchld_nop; ACE_Sig_Action do_sigchld (reinterpret_cast (reinterpret_cast (sigchld_nop_ptr))); 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 () { 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 () { 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) 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 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) : inherit_environment_ (inherit_environment), creation_flags_ (0), avoid_zombies_ (0), #if defined (ACE_WIN32) environment_inherited_ (0), process_attributes_ (0), thread_attributes_ (0), user_token_ (ACE_INVALID_HANDLE), close_user_token_ (false), #else /* ACE_WIN32 */ stdin_ (ACE_INVALID_HANDLE), stdout_ (ACE_INVALID_HANDLE), stderr_ (ACE_INVALID_HANDLE), ruid_ ((uid_t) -1), euid_ ((uid_t) -1), rgid_ ((gid_t) -1), egid_ ((gid_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), 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) { #if defined (ACE_HAS_ALLOC_HOOKS) ACE_ALLOCATOR (command_line_buf_, static_cast(ACE_Allocator::instance()->malloc(sizeof(ACE_TCHAR) * command_line_buf_len))); #else ACE_NEW (command_line_buf_, ACE_TCHAR[command_line_buf_len]); #endif /* ACE_HAS_ALLOC_HOOKS */ command_line_buf_[0] = '\0'; process_name_[0] = '\0'; working_directory_[0] = '\0'; #if defined (ACE_HAS_ALLOC_HOOKS) ACE_ALLOCATOR (environment_buf_, static_cast(ACE_Allocator::instance()->malloc(sizeof(ACE_TCHAR) * env_buf_len))); #else ACE_NEW (environment_buf_, ACE_TCHAR[env_buf_len]); #endif /* ACE_HAS_ALLOC_HOOKS */ #if defined (ACE_HAS_ALLOC_HOOKS) ACE_ALLOCATOR (environment_argv_, static_cast(ACE_Allocator::instance()->malloc(sizeof(ACE_TCHAR*) * max_env_args))); #else ACE_NEW (environment_argv_, ACE_TCHAR *[max_env_args]); #endif /* ACE_HAS_ALLOC_HOOKS */ 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 */ #if defined (ACE_HAS_ALLOC_HOOKS) ACE_ALLOCATOR (command_line_argv_, static_cast(ACE_Allocator::instance()->malloc(sizeof(ACE_TCHAR*) * max_cmdline_args))); #else ACE_NEW (command_line_argv_, ACE_TCHAR *[max_cmdline_args]); #endif /* ACE_HAS_ALLOC_HOOKS */ } #if defined (ACE_WIN32) void ACE_Process_Options::inherit_environment () { // 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 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 const len = ACE_OS::strlen (wta.char_rep ()); size_t const 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 const 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 () { 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; } #ifndef ACE_LACKS_VA_FUNCTIONS 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. int status = ACE_OS::vsnprintf (stack_buf, DEFAULT_COMMAND_LINE_BUF_LEN, format, argp); // End varargs. va_end (argp); if (status == -1) return -1; // 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); std::unique_ptr safe_newformat (newformat); // Add in the variable name. ACE_OS::snprintf (safe_newformat.get (), buflen, ACE_TEXT ("%") ACE_TEXT_PRIs ACE_TEXT ("=%") ACE_TEXT_PRIs, variable_name, 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); std::unique_ptr safe_stack_buf (stack_buf); do { // Must restart varargs on each time through this loop, va_list argp; va_start (argp, format); retval = ACE_OS::vsnprintf (safe_stack_buf.get (), tmp_buflen, safe_newformat.get (), argp); // End varargs. va_end (argp); if (retval > ACE_Utils::truncate_cast (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. va_list argp; va_start (argp, format); retval = ACE_OS::vsprintf (safe_stack_buf.get (), safe_newformat.get (), argp); va_end (argp); if (retval == -1) // vsprintf is failed. return -1; } else // vsnprintf is failed. return -1; } // 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; } #endif // ACE_LACKS_VA_FUNCTIONS 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; // STD handles may have value 0 (not ACE_INVALID_HANDLE) if there is no such // handle in the process. This was observed to occur for stdin in console // processes that were launched from services. In this case we need to make // sure not to return -1 from setting std_in so that we can process std_out // and std_err. if (std_in) { if (!::DuplicateHandle (::GetCurrentProcess (), std_in, ::GetCurrentProcess (), &this->startup_info_.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS)) return -1; } if (std_out) { if (!::DuplicateHandle (::GetCurrentProcess (), std_out, ::GetCurrentProcess (), &this->startup_info_.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS)) return -1; } if (std_err) { 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; } } ACE_Process_Options::~ACE_Process_Options () { release_handles(); #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free(environment_buf_); ACE_Allocator::instance()->free(environment_argv_); #else delete [] environment_buf_; delete [] environment_argv_; #endif /* ACE_HAS_ALLOC_HOOKS */ #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free(command_line_buf_); #else delete [] command_line_buf_; #endif /* ACE_HAS_ALLOC_HOOKS */ ACE::strdelete (command_line_copy_); #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free(command_line_argv_); #else delete [] command_line_argv_; #endif /* ACE_HAS_ALLOC_HOOKS */ #if defined (ACE_WIN32) if (user_token_ != ACE_INVALID_HANDLE && close_user_token_) { ::CloseHandle(user_token_); } #endif /* ACE_WIN32 */ } 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. } #ifndef ACE_LACKS_VA_FUNCTIONS 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; } ACE_OS::vsnprintf (command_line_buf_, command_line_buf_len_, format, argp); // Useless macro. va_end (argp); command_line_argv_calculated_ = false; return 0; } #if defined (ACE_HAS_WCHAR) 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::vsnprintf (anti_clb, this->command_line_buf_len_, 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 */ #endif // ACE_LACKS_VA_FUNCTIONS ACE_TCHAR * ACE_Process_Options::env_buf () { if (environment_buf_[0] == '\0') return 0; else return environment_buf_; } ACE_TCHAR * const * ACE_Process_Options::command_line_argv () { 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) { 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_ALLOC_HOOK_DEFINE(ACE_Managed_Process) void ACE_Managed_Process::unmanage () { delete this; } #if defined (ACE_WIN32) void ACE_Process_Options::set_user_token (HANDLE token, bool close_token) { if (user_token_ != ACE_INVALID_HANDLE && close_user_token_) { ::CloseHandle(user_token_); } user_token_ = token; close_user_token_ = close_token; } #endif /* ACE_WIN32 */ ACE_END_VERSIONED_NAMESPACE_DECL