#include "ace/OS_NS_unistd.h" #if !defined (ACE_HAS_INLINED_OSCALLS) # include "ace/OS_NS_unistd.inl" #endif /* ACE_HAS_INLINED_OSCALLS */ #include "ace/Base_Thread_Adapter.h" #include "ace/OS_NS_stdlib.h" #include "ace/OS_NS_ctype.h" #include "ace/Default_Constants.h" #include "ace/OS_Memory.h" #include "ace/OS_NS_Thread.h" #include "ace/Object_Manager_Base.h" #if defined (ACE_HAS_SYSCTL) # include "ace/os_include/sys/os_sysctl.h" #endif /* ACE_HAS_SYSCTL */ #if defined ACE_HAS_VXCPULIB # include "vxCpuLib.h" # include "cpuset.h" #endif /* ACE_HAS_VXCPULIB */ #include ACE_BEGIN_VERSIONED_NAMESPACE_DECL int ACE_OS::argv_to_string (ACE_TCHAR **argv, ACE_TCHAR *&buf, bool substitute_env_args, bool quote_args) { if (argv == 0 || argv[0] == 0) return 0; int argc; for (argc = 0; argv[argc] != 0; ++argc) continue; return argv_to_string (argc, argv, buf, substitute_env_args, quote_args); } int ACE_OS::argv_to_string (int argc, ACE_TCHAR **argv, ACE_TCHAR *&buf, bool substitute_env_args, bool quote_args) { if (argc <= 0 || argv == 0 || argv[0] == 0) return 0; size_t buf_len = 0; // Determine the length of the buffer. ACE_TCHAR **argv_p = argv; for (int i = 0; i < argc; ++i) { // Account for environment variables. if (substitute_env_args && ACE_OS::strchr (argv[i], ACE_TEXT ('$')) != 0) { if (argv_p == argv) { #if defined (ACE_HAS_ALLOC_HOOKS) argv_p = (ACE_TCHAR **) ACE_Allocator::instance()->malloc (argc * sizeof (ACE_TCHAR *)); #else argv_p = (ACE_TCHAR **) ACE_OS::malloc (argc * sizeof (ACE_TCHAR *)); #endif /* ACE_HAS_ALLOC_HOOKS */ if (argv_p == 0) { errno = ENOMEM; return 0; } ACE_OS::memcpy (argv_p, argv, argc * sizeof (ACE_TCHAR *)); } argv_p[i] = ACE_OS::strenvdup (argv[i]); if (argv_p[i] == 0) { #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free (argv_p); #else ACE_OS::free (argv_p); #endif /* ACE_HAS_ALLOC_HOOKS */ errno = ENOMEM; return 0; } } // If must quote, we only do it if the arg contains spaces, or // is empty. Perhaps a check for other c | ord(c) <= 32 is in // order? if (quote_args && (ACE_OS::strchr (argv_p[i], ACE_TEXT (' ')) != 0 || ACE_OS::strchr (argv_p[i], ACE_TEXT ('\t')) != 0 || ACE_OS::strchr (argv_p[i], ACE_TEXT ('\n')) != 0 || *argv_p[i] == 0)) { if (argv_p == argv) { #if defined (ACE_HAS_ALLOC_HOOKS) argv_p = (ACE_TCHAR **) ACE_Allocator::instance()->malloc (argc * sizeof (ACE_TCHAR *)); #else argv_p = (ACE_TCHAR **) ACE_OS::malloc (argc * sizeof (ACE_TCHAR *)); #endif /* ACE_HAS_ALLOC_HOOKS */ if (argv_p == 0) { errno = ENOMEM; return 0; } ACE_OS::memcpy (argv_p, argv, argc * sizeof (ACE_TCHAR *)); } int quotes = 0; ACE_TCHAR *temp = argv_p[i]; if (ACE_OS::strchr (temp, ACE_TEXT ('"')) != 0) { for (int j = 0; temp[j] != 0; ++j) if (temp[j] == ACE_TEXT ('"')) ++quotes; } argv_p[i] = #if defined (ACE_HAS_ALLOC_HOOKS) (ACE_TCHAR *) ACE_Allocator::instance()->malloc ((ACE_OS::strlen (temp) + quotes + 3) * sizeof (ACE_TCHAR)); #else (ACE_TCHAR *) ACE_OS::malloc ((ACE_OS::strlen (temp) + quotes + 3) * sizeof (ACE_TCHAR)); #endif /* ACE_HAS_ALLOC_HOOKS */ if (argv_p[i] == 0) { #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free (argv_p); #else ACE_OS::free (argv_p); #endif /* ACE_HAS_ALLOC_HOOKS */ errno = ENOMEM; return 0; } ACE_TCHAR *end = argv_p[i]; *end++ = ACE_TEXT ('"'); if (quotes > 0) { for (ACE_TCHAR *p = temp; *p != 0; *end++ = *p++) if (*p == ACE_TEXT ('"')) *end++ = ACE_TEXT ('\\'); *end++ = ACE_TEXT ('\0'); } else end = ACE_OS::strecpy (end, temp); end[-1] = ACE_TEXT ('"'); *end = ACE_TEXT ('\0'); if (temp != argv[i]) #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free (temp); #else ACE_OS::free (temp); #endif /* ACE_HAS_ALLOC_HOOKS */ } buf_len += ACE_OS::strlen (argv_p[i]); // Add one for the extra space between each string. buf_len++; } // Step through all argv params and copy each one into buf; separate // each param with white space. #if defined (ACE_HAS_ALLOC_HOOKS) ACE_ALLOCATOR_RETURN (buf, static_cast(ACE_Allocator::instance()->malloc(sizeof(ACE_TCHAR) * (buf_len + 1))), 0); #else ACE_NEW_RETURN (buf, ACE_TCHAR[buf_len + 1], 0); #endif /* ACE_HAS_ALLOC_HOOKS */ // Initial null charater to make it a null string. buf[0] = ACE_TEXT ('\0'); ACE_TCHAR *end = buf; for (int i = 0; i < argc; ++i) { end = ACE_OS::strecpy (end, argv_p[i]); if (argv_p[i] != argv[i]) #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free (argv_p[i]); #else ACE_OS::free (argv_p[i]); #endif /* ACE_HAS_ALLOC_HOOKS */ // Replace the null char that strecpy put there with white // space. end[-1] = ACE_TEXT (' '); } // Null terminate the string. *end = ACE_TEXT ('\0'); if (argv_p != argv) #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free (argv_p); #else ACE_OS::free (argv_p); #endif /* ACE_HAS_ALLOC_HOOKS */ // The number of arguments. return argc; } int ACE_OS::execl (const char * /* path */, const char * /* arg0 */, ...) { ACE_OS_TRACE ("ACE_OS::execl"); ACE_NOTSUP_RETURN (-1); // Need to write this code. // return ::execv (path, argv); } int ACE_OS::execle (const char * /* path */, const char * /* arg0 */, ...) { ACE_OS_TRACE ("ACE_OS::execle"); ACE_NOTSUP_RETURN (-1); // Need to write this code. // return ::execve (path, argv, envp); } int ACE_OS::execlp (const char * /* file */, const char * /* arg0 */, ...) { ACE_OS_TRACE ("ACE_OS::execlp"); ACE_NOTSUP_RETURN (-1); // Need to write this code. // return ::execvp (file, argv); } pid_t ACE_OS::fork (const ACE_TCHAR *program_name) { ACE_OS_TRACE ("ACE_OS::fork"); # if defined (ACE_LACKS_FORK) ACE_UNUSED_ARG (program_name); ACE_NOTSUP_RETURN (pid_t (-1)); # else pid_t const pid = ::fork (); #if !defined (ACE_HAS_MINIMAL_ACE_OS) && !defined (ACE_HAS_THREADS) // ACE_Base_Thread_Adapter::sync_log_msg() is used to update the // program name and process id in ACE's log framework. However, we // can't invoke it from (the child process of) threaded programs // because it calls async signal unsafe functions, which will result // in undefined behavior (only async signal safe functions can be // called after fork() until an exec()). // // This is no great loss. Using the ACE log framework in the child // process will undoubtedly call async signal unsafe functions too. // So it doesn't really matter that the program name and process id // will not be updated. if (pid == 0) ACE_Base_Thread_Adapter::sync_log_msg (program_name); #else ACE_UNUSED_ARG (program_name); #endif /* ! ACE_HAS_MINIMAL_ACE_OS && !ACE_HAS_THREADS */ return pid; # endif /* ACE_WIN32 */ } // Create a contiguous command-line argument buffer with each arg // separated by spaces. pid_t ACE_OS::fork_exec (ACE_TCHAR *argv[]) { # if defined (ACE_WIN32) ACE_TCHAR *buf = 0; std::unique_ptr safe_ptr (buf); if (ACE_OS::argv_to_string (argv, buf) != -1) { PROCESS_INFORMATION process_info; ACE_TEXT_STARTUPINFO startup_info; ACE_OS::memset ((void *) &startup_info, 0, sizeof startup_info); startup_info.cb = sizeof startup_info; if (ACE_TEXT_CreateProcess (0, buf, 0, // No process attributes. 0, // No thread attributes. TRUE, // Allow handle inheritance. 0, // Don't create a new console window. 0, // No environment. 0, // No current directory. &startup_info, &process_info)) { // Free resources allocated in kernel. ACE_OS::close (process_info.hThread); ACE_OS::close (process_info.hProcess); // Return new process id. return process_info.dwProcessId; } } // CreateProcess failed. return -1; # else pid_t const result = ACE_OS::fork (); # if defined (ACE_USES_WCHAR) // Wide-char builds need to convert the command-line args to // narrow char strings for execv (). char **cargv = 0; int arg_count; # endif /* ACE_HAS_WCHAR */ switch (result) { case static_cast(-1): // Error. return -1; case 0: // Child process. # if defined (ACE_USES_WCHAR) for (arg_count = 0; argv[arg_count] != 0; ++arg_count) ; ++arg_count; // Need a 0-pointer end-of-array marker ACE_NEW_NORETURN (cargv, char*[arg_count]); if (cargv == 0) ACE_OS::exit (errno); --arg_count; // Back to 0-indexed cargv[arg_count] = 0; while (--arg_count >= 0) cargv[arg_count] = ACE_Wide_To_Ascii::convert (argv[arg_count]); // Don't worry about freeing the cargv or the strings it points to. // Either the process will be replaced, or we'll exit. if (ACE_OS::execv (cargv[0], cargv) == -1) ACE_OS::exit (errno); # else if (ACE_OS::execv (argv[0], argv) == -1) { // The OS layer should not print stuff out // ACELIB_ERROR ((LM_ERROR, // "%p Exec failed\n")); // If the execv fails, this child needs to exit. ACE_OS::exit (errno); } # endif /* ACE_HAS_WCHAR */ return result; default: // Server process. The fork succeeded. return result; } # endif /* ACE_WIN32 */ } long ACE_OS::num_processors () { ACE_OS_TRACE ("ACE_OS::num_processors"); #if defined (ACE_WIN32) SYSTEM_INFO sys_info; ::GetSystemInfo (&sys_info); return sys_info.dwNumberOfProcessors; #elif defined (ACE_HAS_VXCPULIB) return vxCpuConfiguredGet(); #elif defined (_SC_NPROCESSORS_CONF) return ::sysconf (_SC_NPROCESSORS_CONF); #elif defined (ACE_HAS_SYSCTL) int num_processors = 0; int mib[2] = { CTL_HW, HW_NCPU }; size_t len = sizeof (num_processors); if (::sysctl (mib, 2, &num_processors, &len, 0, 0) != -1) return num_processors; else return -1; #else ACE_NOTSUP_RETURN (-1); #endif } long ACE_OS::num_processors_online () { ACE_OS_TRACE ("ACE_OS::num_processors_online"); #if defined (ACE_WIN32) SYSTEM_INFO sys_info; ::GetSystemInfo (&sys_info); long active_processors = 0; DWORD_PTR mask = sys_info.dwActiveProcessorMask; while (mask != 0) { if (mask & 1) ++active_processors; mask >>= 1; } return active_processors; #elif defined (ACE_HAS_VXCPULIB) long num_cpu = 0; cpuset_t cpuset; CPUSET_ZERO (cpuset); cpuset = vxCpuEnabledGet(); unsigned int const maxcpu = vxCpuConfiguredGet(); for (unsigned int i =0; i < maxcpu; i++) { if (CPUSET_ISSET (cpuset, i)) { ++num_cpu; } } return num_cpu; #elif defined (_SC_NPROCESSORS_ONLN) return ::sysconf (_SC_NPROCESSORS_ONLN); #elif defined (ACE_HAS_SYSCTL) int num_processors; int mib[2] = { CTL_HW, HW_NCPU }; size_t len = sizeof (num_processors); if (::sysctl (mib, 2, &num_processors, &len, 0, 0) != -1) return num_processors; else return -1; #else ACE_NOTSUP_RETURN (-1); #endif } ssize_t ACE_OS::read_n (ACE_HANDLE handle, void *buf, size_t len, size_t *bt) { size_t temp; size_t &bytes_transferred = bt == 0 ? temp : *bt; ssize_t n = 0; for (bytes_transferred = 0; bytes_transferred < len; bytes_transferred += n) { n = ACE_OS::read (handle, (char *) buf + bytes_transferred, len - bytes_transferred); if (n == -1 || n == 0) { return n; } } return ACE_Utils::truncate_cast (bytes_transferred); } ssize_t ACE_OS::pread (ACE_HANDLE handle, void *buf, size_t nbytes, ACE_OFF_T offset) { # if defined (ACE_HAS_P_READ_WRITE) # if defined (ACE_WIN32) ACE_OS_GUARD // Remember the original file pointer position LONG original_high_position = 0; DWORD original_low_position = ::SetFilePointer (handle, 0, &original_high_position, FILE_CURRENT); if (original_low_position == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) { ACE_OS::set_errno_to_last_error (); return -1; } // Go to the correct position LONG low_offset = ACE_LOW_PART (offset); LONG high_offset = ACE_HIGH_PART (offset); DWORD altered_position = ::SetFilePointer (handle, low_offset, &high_offset, FILE_BEGIN); if (altered_position == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) { ACE_OS::set_errno_to_last_error (); return -1; } DWORD bytes_read; # if defined (ACE_HAS_WIN32_OVERLAPPED_IO) OVERLAPPED overlapped; overlapped.Internal = 0; overlapped.InternalHigh = 0; overlapped.Offset = low_offset; overlapped.OffsetHigh = high_offset; overlapped.hEvent = 0; BOOL result = ::ReadFile (handle, buf, static_cast (nbytes), &bytes_read, &overlapped); if (result == FALSE) { if (::GetLastError () != ERROR_IO_PENDING) return -1; else { result = ::GetOverlappedResult (handle, &overlapped, &bytes_read, TRUE); if (result == FALSE) return -1; } } # else /* ACE_HAS_WIN32_OVERLAPPED_IO */ BOOL result = ::ReadFile (handle, buf, nbytes, &bytes_read, 0); if (result == FALSE) return -1; # endif /* ACE_HAS_WIN32_OVERLAPPED_IO */ // Reset the original file pointer position if (::SetFilePointer (handle, original_low_position, &original_high_position, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) { ACE_OS::set_errno_to_last_error (); return -1; } return (ssize_t) bytes_read; # else /* ACE_WIN32 */ return ::pread (handle, buf, nbytes, offset); # endif /* ACE_WIN32 */ # else /* ACE_HAS_P_READ_WRITE */ ACE_OS_GUARD // Remember the original file pointer position ACE_OFF_T original_position = ACE_OS::lseek (handle, 0, SEEK_CUR); if (original_position == -1) return -1; // Go to the correct position ACE_OFF_T altered_position = ACE_OS::lseek (handle, offset, SEEK_SET); if (altered_position == -1) return -1; ssize_t const bytes_read = ACE_OS::read (handle, buf, nbytes); if (bytes_read == -1) return -1; if (ACE_OS::lseek (handle, original_position, SEEK_SET) == -1) return -1; return bytes_read; # endif /* ACE_HAS_P_READ_WRITE */ } ssize_t ACE_OS::pwrite (ACE_HANDLE handle, const void *buf, size_t nbytes, ACE_OFF_T offset) { # if defined (ACE_HAS_P_READ_WRITE) # if defined (ACE_WIN32) ACE_OS_GUARD // Remember the original file pointer position LONG original_high_position = 0; DWORD original_low_position = ::SetFilePointer (handle, 0, &original_high_position, FILE_CURRENT); if (original_low_position == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) { ACE_OS::set_errno_to_last_error (); return -1; } DWORD bytes_written; LONG low_offset = ACE_LOW_PART (offset); LONG high_offset = ACE_HIGH_PART (offset); # if defined (ACE_HAS_WIN32_OVERLAPPED_IO) OVERLAPPED overlapped; overlapped.Internal = 0; overlapped.InternalHigh = 0; overlapped.Offset = low_offset; overlapped.OffsetHigh = high_offset; overlapped.hEvent = 0; BOOL result = ::WriteFile (handle, buf, static_cast (nbytes), &bytes_written, &overlapped); if (result == FALSE) { if (::GetLastError () != ERROR_IO_PENDING) { return -1; } else { result = ::GetOverlappedResult (handle, &overlapped, &bytes_written, TRUE); if (result == FALSE) return -1; } } # else /* ACE_HAS_WIN32_OVERLAPPED_IO */ if (::SetFilePointer (handle, low_offset, &high_offset, FILE_BEGIN) == INVALID_SET_FILE_POINTER && ::GetLastError () != NO_ERROR) { ACE_OS::set_errno_to_last_error (); return -1; } BOOL result = ::WriteFile (handle, buf, nbytes, &bytes_written, 0); if (result == FALSE) return -1; # endif /* ACE_HAS_WIN32_OVERLAPPED_IO */ // Reset the original file pointer position if (::SetFilePointer (handle, original_low_position, &original_high_position, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) { ACE_OS::set_errno_to_last_error (); return -1; } return (ssize_t) bytes_written; # else /* ACE_WIN32 */ # if defined (ACE_HAS_NON_CONST_PWRITE) return ::pwrite (handle, const_cast (buf), nbytes, offset); # else return ::pwrite (handle, buf, nbytes, offset); # endif # endif /* ACE_WIN32 */ # else /* ACE_HAS_P_READ_WRITE */ ACE_OS_GUARD // Remember the original file pointer position ACE_OFF_T original_position = ACE_OS::lseek (handle, 0, SEEK_CUR); if (original_position == -1) return -1; // Go to the correct position ACE_OFF_T altered_position = ACE_OS::lseek (handle, offset, SEEK_SET); if (altered_position == -1) return -1; ssize_t const bytes_written = ACE_OS::write (handle, buf, nbytes); if (bytes_written == -1) return -1; if (ACE_OS::lseek (handle, original_position, SEEK_SET) == -1) return -1; return bytes_written; # endif /* ACE_HAS_P_READ_WRITE */ } int ACE_OS::string_to_argv (ACE_TCHAR *buf, int &argc, ACE_TCHAR **&argv, bool substitute_env_args) { // Reset the number of arguments argc = 0; if (buf == 0) return -1; ACE_TCHAR *cp = buf; // First pass: count arguments. // '#' is the start-comment token.. while (*cp != ACE_TEXT ('\0') && *cp != ACE_TEXT ('#')) { // Skip whitespace.. while (ACE_OS::ace_isspace (*cp)) ++cp; // Increment count and move to next whitespace.. if (*cp != ACE_TEXT ('\0')) ++argc; while (*cp != ACE_TEXT ('\0') && !ACE_OS::ace_isspace (*cp)) { // Grok quotes.... if (*cp == ACE_TEXT ('\'') || *cp == ACE_TEXT ('"')) { ACE_TCHAR quote = *cp; // Scan past the string.. for (++cp; *cp != ACE_TEXT ('\0') && (*cp != quote || cp[-1] == ACE_TEXT ('\\')); ++cp) continue; // '\0' implies unmatched quote.. if (*cp == ACE_TEXT ('\0')) { --argc; break; } else ++cp; } else ++cp; } } // Second pass: copy arguments. ACE_TCHAR arg[ACE_DEFAULT_ARGV_BUFSIZ]; ACE_TCHAR *argp = arg; // Make sure that the buffer we're copying into is always large // enough. if (cp - buf >= ACE_DEFAULT_ARGV_BUFSIZ) #if defined (ACE_HAS_ALLOC_HOOKS) ACE_ALLOCATOR_RETURN(argp, static_cast(ACE_Allocator::instance()->malloc(sizeof (ACE_TCHAR) * (cp - buf + 1))), -1); #else ACE_NEW_RETURN (argp, ACE_TCHAR[cp - buf + 1], -1); #endif /* ACE_HAS_ALLOC_HOOKS */ // Make a new argv vector of argc + 1 elements. #if defined (ACE_HAS_ALLOC_HOOKS) ACE_ALLOCATOR_RETURN(argv, static_cast(ACE_Allocator::instance()->malloc(sizeof (ACE_TCHAR*) * (argc + 1))), -1); #else ACE_NEW_RETURN (argv, ACE_TCHAR *[argc + 1], -1); #endif /* ACE_HAS_ALLOC_HOOKS */ ACE_TCHAR *ptr = buf; for (int i = 0; i < argc; ++i) { // Skip whitespace.. while (ACE_OS::ace_isspace (*ptr)) ++ptr; // Copy next argument and move to next whitespace.. cp = argp; while (*ptr != ACE_TEXT ('\0') && !ACE_OS::ace_isspace (*ptr)) if (*ptr == ACE_TEXT ('\'') || *ptr == ACE_TEXT ('"')) { ACE_TCHAR quote = *ptr++; while (*ptr != ACE_TEXT ('\0') && (*ptr != quote || ptr[-1] == ACE_TEXT ('\\'))) { if (*ptr == quote && ptr[-1] == ACE_TEXT ('\\')) --cp; *cp++ = *ptr++; } if (*ptr == quote) ++ptr; } else *cp++ = *ptr++; *cp = ACE_TEXT ('\0'); // Check for environment variable substitution here. if (substitute_env_args) { argv[i] = ACE_OS::strenvdup (argp); if (argv[i] == 0) { if (argp != arg) #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free(argp); #else delete [] argp; #endif /* ACE_HAS_ALLOC_HOOKS */ errno = ENOMEM; return -1; } } else { argv[i] = ACE_OS::strdup (argp); if (argv[i] == 0) { if (argp != arg) { #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free(argp); #else delete [] argp; #endif /* ACE_HAS_ALLOC_HOOKS */ } errno = ENOMEM; return -1; } } } if (argp != arg) { #if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance()->free(argp); #else delete [] argp; #endif /* ACE_HAS_ALLOC_HOOKS */ } argv[argc] = 0; return 0; } // Write bytes from to (uses the // system call on UNIX and the call on Win32). ssize_t ACE_OS::write_n (ACE_HANDLE handle, const void *buf, size_t len, size_t *bt) { size_t temp; size_t &bytes_transferred = bt == 0 ? temp : *bt; ssize_t n; for (bytes_transferred = 0; bytes_transferred < len; bytes_transferred += n) { n = ACE_OS::write (handle, (char *) buf + bytes_transferred, len - bytes_transferred); if (n == -1 || n == 0) { return n; } } return ACE_Utils::truncate_cast (bytes_transferred); } ACE_END_VERSIONED_NAMESPACE_DECL