Return-Path: Delivered-To: apmail-incubator-stdcxx-commits-archive@www.apache.org Received: (qmail 61412 invoked from network); 18 Aug 2006 00:51:28 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 18 Aug 2006 00:51:28 -0000 Received: (qmail 1842 invoked by uid 500); 18 Aug 2006 00:51:28 -0000 Delivered-To: apmail-incubator-stdcxx-commits-archive@incubator.apache.org Received: (qmail 1821 invoked by uid 500); 18 Aug 2006 00:51:28 -0000 Mailing-List: contact stdcxx-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: stdcxx-dev@incubator.apache.org Delivered-To: mailing list stdcxx-commits@incubator.apache.org Received: (qmail 1805 invoked by uid 99); 18 Aug 2006 00:51:28 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 17 Aug 2006 17:51:28 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 17 Aug 2006 17:51:26 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id A449D1A981D; Thu, 17 Aug 2006 17:51:06 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r432452 - in /incubator/stdcxx/trunk/util: cmdopt.cpp exec.cpp exec.h runall.cpp Date: Fri, 18 Aug 2006 00:51:05 -0000 To: stdcxx-commits@incubator.apache.org From: sebor@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060818005106.A449D1A981D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: sebor Date: Thu Aug 17 17:51:05 2006 New Revision: 432452 URL: http://svn.apache.org/viewvc?rev=432452&view=rev Log: 2006-08-17 Andrew Black * exec.h [_WIN32 || _WIN64] (exec_attrs): Alter structure definition for windows. * exec.cpp [_WIN32 || _WIN64]: Use windows.h and process.h in place of unistd.h and sys/wait.h on windows. [!_WIN32 && !_WIN64] (handle_alrm, wait_for_child, open_input, replace_file, exec_file): Reduce scope of (existing) functions to non-windows platforms only. [_WIN32 || _WIN64] (open_input, merge_argv, warn_last_error, exec_file): Define implementation of named functions for windows platforms only. * cmdopt.cpp [_WIN32 || _WIN64]: Use windows.h in place of unistd.h on windows. (escape_code, default_path_sep, suffix_sep, exe_suffix_len): Alter values for windows. (rw_sleep, rw_signal): Define platform independent wrapper for sleep/Sleep and sigaction/signal. (eval_options): Use above. * runall.cpp [!_WIN32 && !_WIN64]: Don't include sys/wait.h on windows. (S_IXUSR, S_IXGRP, S_IXOTH): Define values if undefined (for windows). (check_target_ok) [_WIN32 || _WIN64]: Make object file comparison operating system specific. (process_results) [_WIN32 || _WIN64]: Make error state processing code operating system specific. (rw_basename) [_WIN32 || _WIN64]: Treat slash as an additional path separator on windows. Modified: incubator/stdcxx/trunk/util/cmdopt.cpp incubator/stdcxx/trunk/util/exec.cpp incubator/stdcxx/trunk/util/exec.h incubator/stdcxx/trunk/util/runall.cpp Modified: incubator/stdcxx/trunk/util/cmdopt.cpp URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/util/cmdopt.cpp?rev=432452&r1=432451&r2=432452&view=diff ============================================================================== --- incubator/stdcxx/trunk/util/cmdopt.cpp (original) +++ incubator/stdcxx/trunk/util/cmdopt.cpp Thu Aug 17 17:51:05 2006 @@ -35,7 +35,11 @@ #include /* for *printf, fputs */ #include /* for exit */ #include /* for str* */ -#include /* for sleep */ +#if !defined (_WIN32) && !defined (_WIN64) +# include /* for sleep */ +#else +# include /* for Sleep */ +#endif /* _WIN{32,64} */ #include "exec.h" #include "util.h" @@ -50,10 +54,17 @@ const char* in_root = ""; /**< Root directory for input/reference files. */ const char* exe_name; /**< Alias for process argv [0]. */ const char* target_name; +#if !defined (_WIN32) && !defined (_WIN64) const char escape_code = '\\'; const char default_path_sep = '/'; const char suffix_sep = '.'; const size_t exe_suffix_len = 0; +#else +const char escape_code = '^'; +const char default_path_sep = '\\'; +const char suffix_sep = '.'; +const size_t exe_suffix_len = 4; /* strlen(".exe") == 4 */ +#endif static const char usage_text[] = { @@ -88,6 +99,35 @@ " '--option=value' or '--option value'.\n" }; +#if !defined (_WIN32) && !defined (_WIN64) +static void +rw_sleep (int seconds) +{ + sleep (seconds); +} + +static int +rw_signal (int signo, void (*func)(int)) +{ + struct sigaction act; + memset (&act, 0, sizeof act); + act.sa_handler = func; + return 0 > sigaction (signo, &act, 0); +} +#else +static void +rw_sleep (int seconds) +{ + Sleep (seconds * 1000); +} + +static int +rw_signal (int signo, void (*func)(int)) +{ + return SIG_ERR == signal (signo, func); +} +#endif + /** Display command line switches for program and terminate. @@ -308,7 +348,7 @@ if (optarg && *optarg) { const long nsec = strtol (optarg, &end, 10); if ('\0' == *end && 0 <= nsec && !errno) { - sleep (nsec); + rw_sleep (nsec); break; } } @@ -334,11 +374,8 @@ if (optarg && *optarg) { const long signo = get_signo (optarg); if (0 <= signo) { - struct sigaction act; - memset (&act, 0, sizeof act); - act.sa_handler = SIG_IGN; - if (0 > sigaction (signo, &act, 0)) - terminate (1, "sigaction(%s, ...) failed: %s\n", + if (rw_signal (signo, SIG_IGN)) + terminate (1, "rw_signal(%s, ...) failed: %s\n", get_signame (signo), strerror (errno)); break; } Modified: incubator/stdcxx/trunk/util/exec.cpp URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/util/exec.cpp?rev=432452&r1=432451&r2=432452&view=diff ============================================================================== --- incubator/stdcxx/trunk/util/exec.cpp (original) +++ incubator/stdcxx/trunk/util/exec.cpp Thu Aug 17 17:51:05 2006 @@ -37,10 +37,15 @@ #include #include /* for str*, mem* */ -#include /* for close, dup, exec, fork */ +#if !defined (_WIN32) && !defined (_WIN64) +# include /* for close, dup, exec, fork */ +# include +#else +# include /* for PROCESS_INFORMATION, ... */ +# include /* for CreateProcess, ... */ +#endif #include /* for S_* */ #include -#include #include "cmdopt.h" #include "util.h" @@ -354,6 +359,7 @@ return def; } +#if !defined (_WIN32) && !defined (_WIN64) /** Callback used to set the alarm_timeout flag in response to recieving the signal SIGALRM @@ -714,3 +720,306 @@ /* parent */ return wait_for_child (child_pid); } +#else /* _WIN{32,64} */ +/** + Opens an input file, based on exec_name, using the child_sa security + setting. + + Takes an executable name and security setting, and tries to open an input + file based on these variables and the value of the in_root global variable. + If a file is found in neither of two locations derived from these + variables, or if in_root is a null string, it returns null. + If a file system error occurs when opening a file, INVALID_HANDLE_VALUE + is returned (and should be checked for). + + Source file locations: + - [in_root]/manual/in/[exec_name].in + - [in_root]/tutorial/in/[exec_name].in + + @param exec_name the name of executable being run + @param child_sa pointer to a SECURITY_ATTRIBUTES structure + @returns the file descriptor of the opened file + @see in_root +*/ +static HANDLE +open_input (const char* exec_name, SECURITY_ATTRIBUTES* child_sa) +{ + const size_t root_len = strlen (in_root); + HANDLE intermit; + DWORD error; + char* tmp_name; + + assert (0 != exec_name); + assert (0 != in_root); + assert (0 != child_sa); + + if (!root_len) + return 0; + + /* Try in_root\manual\in\exec_name.in */ + tmp_name = reference_name ("manual", "in"); + + intermit = CreateFile (tmp_name, GENERIC_READ, FILE_SHARE_READ, child_sa, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + error = GetLastError (); + /* If we found the file, return the descriptor */ + if (INVALID_HANDLE_VALUE != intermit || (2 != error && 3 != error)) { + free (tmp_name); + return intermit; + } + + /* Try in_root\tutorial\in\exec_name.in */ + free (tmp_name); + tmp_name = reference_name ("tutorial", "in"); + intermit = CreateFile (tmp_name, GENERIC_READ, FILE_SHARE_READ, child_sa, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + /* If we didn't find the file, null out the handle to return */ + error = GetLastError (); + if (INVALID_HANDLE_VALUE == intermit && (2 == error || 3 == error)) { + intermit = 0; + } + + free (tmp_name); + return intermit; +} + +/** + Convert an argv array into a string that can be passed to CreateProcess. + + This method allocates memory which the caller is responsible for free ()ing. + The provided argv array is converted into a series of quoted strings. + + @param argv argv array to convert + @return allocated array with converted contents +*/ +static char* +merge_argv (char** argv) +{ + size_t len = 0; + char** opts; + char* term; + char* merge; + char* pos; + + assert (0 != argv); + + for (opts = argv; *opts; ++opts) { + len += 3; /* for open ", close " and trailing space or null */ + for (term = *opts; *term; ++term) { + if ('"' == *term || escape_code == *term) + ++len; /* Escape embedded "s and ^s*/ + ++len; + } + } + + pos = merge = (char*)RW_MALLOC (len); + for (opts = argv; *opts; ++opts) { + *(pos++) = '"'; + for (term = *opts; *term; ++term) { + if ('"' == *term || escape_code == *term) + *(pos++) = escape_code; /* Escape embedded "s and ^s*/ + *(pos++) = *term; + } + *(pos++) = '"'; + *(pos++) = ' '; + } + *(pos-1) = '\0'; /* convert trailing space to null */ + return merge; +} + +/** + Wrapper function around warn for windows native API calls. + + @param action Human understandable description of failed action. + @return Value of GetLastError. +*/ +static DWORD +warn_last_error (const char* action) +{ + DWORD error = GetLastError (); + + if (error) { + LPTSTR error_text = 0; + if (FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + error_text, 0, NULL)) { + warn ("%s failed with error %d: %s\n", action, error, error_text); + LocalFree (error_text); + } + else { + warn ("%s failed with error %d. Additionally, FormatMessage " + "failed with error %d.\n", action, error, GetLastError ()); + } + } + return error; +} + +/** + Entry point to the child process (watchdog) subsystem. + + This method creates a process using the windows CreateProcess API call. + The startup context for this process is based on the context of the exec + utility, but with the standard * file handles redirected. The execution of + the process is monitored with the WaitForSingleObject API call. If the + process doesn't complete within the allowed timeout, it is then killed. On + Windows NT systems, a soft kill of the process (via the + GenerateConsoleCtrlEvent API call) are first attempted attempted. The + process is then hard killed via the TerminateProcess API call. + + @param exec_name name of the child executable + @param argv argv array for child process + @return structure describing how the child procees exited + @see wait_for_child () +*/ +struct exec_attrs +exec_file (char** argv) +{ + char* merged; + PROCESS_INFORMATION child; + OSVERSIONINFO OSVer; + STARTUPINFO context; + SECURITY_ATTRIBUTES child_sa = /* SA for inheritable handle. */ + {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; + struct exec_attrs status; + const DWORD real_timeout = (timeout > 0) ? timeout * 1000 : INFINITE; + DWORD wait_code; + + assert (0 != argv); + + memset (&status, 0, sizeof status); + + OSVer.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + if (0 == GetVersionEx (&OSVer)) { + status.status = -1; + status.error = warn_last_error ("Retrieving host version"); + return status; + } + /* Borrow our startup info */ + GetStartupInfo (&context); + context.dwFlags = STARTF_USESTDHANDLES; + + /* Create I/O handles */ + { + /* Output redirection */ + char* const tmp_name = output_name (argv [0]); + + context.hStdOutput = CreateFile (tmp_name, GENERIC_WRITE, + FILE_SHARE_WRITE, &child_sa, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == context.hStdOutput) { + status.status = -1; + status.error = warn_last_error ("Opening child output stream"); + return status; + } + + context.hStdError = context.hStdOutput; + free (tmp_name); + + /* Input redirection */ + context.hStdInput = open_input (target_name, &child_sa); + if (INVALID_HANDLE_VALUE == context.hStdInput) { + CloseHandle (context.hStdOutput); + status.status = -1; + status.error = warn_last_error ("Opening child input stream"); + return status; + } + } + + merged = merge_argv (argv); + + /* Create the child process */ + if (0 == CreateProcess (argv [0], merged, 0, 0, 1, + CREATE_NEW_PROCESS_GROUP, 0, 0, &context, &child)) { + /* record the status if we failed to create the process */ + status.status = -1; + status.error = warn_last_error ("Creating child process");; + } + + /* Clean up handles */ + if (context.hStdInput) + if (0 == CloseHandle (context.hStdInput)) + warn_last_error ("Closing child input stream"); + if (0 == CloseHandle (context.hStdOutput)) + warn_last_error ("Closing child output stream"); + + /* Clean up argument string*/ + free (merged); + + /* Return if we failed to create the child process */ + if (-1 == status.status) + return status; + + /* Wait for the child process to terminate */ + wait_code = WaitForSingleObject (child.hProcess, real_timeout); + if (WAIT_TIMEOUT != wait_code) { + if (WAIT_OBJECT_0 == wait_code) { + if (0 == GetExitCodeProcess (child.hProcess, &status.status)) { + warn_last_error ("Retrieving child process exit code"); + status.status = -1; + } + status.error = 0; + } + else { + status.status = -1; + status.error = warn_last_error ("Waiting for child process"); + } + return status; + } + + /* Try to soft kill child process group if it didn't terminate, but only + on NT */ + if (VER_PLATFORM_WIN32_NT == OSVer.dwPlatformId) { + if (0 == GenerateConsoleCtrlEvent (CTRL_C_EVENT, child.dwProcessId)) + warn_last_error ("Sending child process Control-C"); + + wait_code = WaitForSingleObject (child.hProcess, 1000); + if (WAIT_TIMEOUT != wait_code) { + if (WAIT_OBJECT_0 == wait_code) { + if (0 == GetExitCodeProcess (child.hProcess, + &status.status)) { + warn_last_error ("Retrieving child process exit code"); + status.status = -1; + } + status.error = 1; + } + else { + status.status = -1; + status.error = warn_last_error ("Waiting for child process"); + } + return status; + } + + if (0 == GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, + child.dwProcessId)) + warn_last_error ("Sending child process Control-Break"); + + if (WAIT_TIMEOUT != wait_code) { + if (WAIT_OBJECT_0 == wait_code) { + if (0 == GetExitCodeProcess (child.hProcess, + &status.status)) { + warn_last_error ("Retrieving child process exit code"); + status.status = -1; + } + status.error = 2; + } + else { + status.status = -1; + status.error = warn_last_error ("Waiting for child process"); + } + return status; + } + } + /* Then hard kill the child process */ + if (0 == TerminateProcess (child.hProcess, 3)) + warn_last_error ("Terminating child process"); + if (0 == GetExitCodeProcess (child.hProcess, &status.status)) { + warn_last_error ("Retrieving child process exit code"); + status.status = -1; + } + status.error = 3; + return status; +} +#endif /* _WIN{32,64} */ Modified: incubator/stdcxx/trunk/util/exec.h URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/util/exec.h?rev=432452&r1=432451&r2=432452&view=diff ============================================================================== --- incubator/stdcxx/trunk/util/exec.h (original) +++ incubator/stdcxx/trunk/util/exec.h Thu Aug 17 17:51:05 2006 @@ -28,8 +28,14 @@ #define RW_EXEC_H struct exec_attrs { +#if !defined (_WIN32) && !defined (_WIN64) int status; int killed; +#else + /* AKA DWORD */ + unsigned long status; + unsigned long error; +#endif /* _WIN{32,64} */ }; int get_signo (const char* signame); Modified: incubator/stdcxx/trunk/util/runall.cpp URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/util/runall.cpp?rev=432452&r1=432451&r2=432452&view=diff ============================================================================== --- incubator/stdcxx/trunk/util/runall.cpp (original) +++ incubator/stdcxx/trunk/util/runall.cpp Thu Aug 17 17:51:05 2006 @@ -33,7 +33,9 @@ #include /* for isspace */ #include #include -#include /* for WIFEXITED(), ... */ +#if !defined (_WIN32) && !defined (_WIN64) +# include /* for WIFEXITED(), ... */ +#endif #include "cmdopt.h" #include "exec.h" @@ -44,6 +46,18 @@ # define ENOENT 2 #endif // ENOENT +#ifndef S_IXUSR +# define S_IXUSR 0100 +#endif // S_IXUSR + +#ifndef S_IXGRP +# define S_IXGRP 0010 +#endif // S_IXGRP + +#ifndef S_IXOTH +# define S_IXOTH 0001 +#endif // S_IXOTH + /** Utility function to rework the argv array @@ -231,10 +245,29 @@ return 0; } - /* Otherwise, check for the .o file */ +#if !defined (_WIN32) && !defined (_WIN64) + /* Otherwise, check for the .o file on non-windows systems */ tmp_name = (char*)RW_MALLOC (path_len + 3); memcpy (tmp_name, target, path_len + 1); strcat (tmp_name,".o"); +#else + /* Or the target\target.obj file on windows systems*/ + { + size_t target_len = strlen (target_name); + size_t tmp_len = path_len + target_len - 2; + /* - 2 comes from removing 4 characters (extra .exe) and + adding 2 characters (\ directory seperator and trailing + null) */ + tmp_name = (char*)RW_MALLOC (tmp_len); + memcpy (tmp_name, target, path_len - 4); + tmp_name [path_len - 4] = default_path_sep; + memcpy (tmp_name + path_len - 3, target_name, target_len); + tmp_name [tmp_len - 4] = 'o'; + tmp_name [tmp_len - 3] = 'b'; + tmp_name [tmp_len - 2] = 'j'; + tmp_name [tmp_len - 2] = '\0'; + } +#endif /* _WIN{32,64} */ if (0 > stat (tmp_name, &file_info)) { if (ENOENT != errno) { @@ -286,6 +319,7 @@ if (0 == result->status) { parse_output (target); } +#if !defined (_WIN32) && !defined (_WIN64) else if (WIFEXITED (result->status)) { const int retcode = WEXITSTATUS (result->status); switch (retcode) { @@ -311,6 +345,14 @@ else { printf ("(%d|%d)\n", result->status, result->killed); } +#else + else if (-1 == result->status) + puts (" I/O"); + else if (result->error) + puts ("KILLED"); + else + printf ("%6d\n", result->status); +#endif /* _WIN{32,64} */ } /** @@ -335,7 +377,11 @@ assert (0 != path); for (mark = pos = path; '\0' != *pos; ++pos) +#if !defined (_WIN32) && !defined (_WIN64) mark = (default_path_sep == *pos) ? pos + 1 : mark; +#else + mark = (default_path_sep == *pos || '/' == *pos) ? pos + 1 : mark; +#endif /* _WIN{32,64} */ return mark; }