> -----Original Message-----
> From: Martin Sebor [mailto:[EMAIL PROTECTED]
> Sent: Friday, July 21, 2006 3:34 AM
> To: [email protected]
> Subject: Re: string methods thread safety
>
> I would move these two functions to a different header/source
> file, say rw_process.h and process.cpp. We can also move
> rw_system() to the same files and get rid of system.h and system.cpp.
Done.
> > // returns pid of the created process
> > // returns -1 if failed
> > // note: argv[0] should be equal to path _TEST_EXPORT rw_pid_t
> > rw_process_create (const char* path, char* const argv []);
>
> I'm not sure about the second argument. Is it the most
> convenient interface? Wouldn't something closer to
> rw_system() be easier to use? (If so, you might want to move
> rw_process_create to the .cpp file and make it static and
> call it from the new vararg function).
For a while I implemented the two overloads: the argv overload
(used by string mt test), and the vararg overload. Should we keep
the both overloads? The advantages of the argv overload for string mt
test I have described in the previous letter (Sent: Wednesday, July 26,
2006 7:09 PM; Subject: RE: string methods thread safety).
> > // returns pid of the specified process // returns -1 if failed //
> > result is a pointer to a buffer where the result code // of the
> > specified process will be stored, or NULL _TEST_EXPORT rw_pid_t
> > rw_process_join (rw_pid_t pid, int* result);
>
> Okay, this is close to waitpid() that I would suggest to
> rename it to simply rw_waitpid() :)
Done.
The test exercising the rw_process_create() overloads and rw_waitpid()
implemented also (0.process.cpp). This test executes the self with some
parameters, then the executed copy compares the parameters with expected
and returns the result. The one problem is confusing rw_error() output
when I test the expected errno value in cases when rw_process_create()
and rw_waitpid() should fail. Maybe that cases are not necessary?
The files are attached.
Farid.
/************************************************************************
*
* rw_process.h - declarations of testsuite process helpers
*
* $Id: rw_process.h
*
************************************************************************
*
* Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave
* Software division. Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0. Unless required by
* applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under
* the License.
*
**************************************************************************/
#ifndef RW_PROCESS_H_INCLUDED
#define RW_PROCESS_H_INCLUDED
#include <testdefs.h>
typedef long rw_pid_t;
_TEST_EXPORT int
rw_system (const char *cmd, ...);
// returns pid of the created process or -1 on error
// (in which case errno is set to an appropriate value)
_TEST_EXPORT rw_pid_t
rw_process_create (const char*, ...);
// note: argv[0] should be equal to path
// returns pid of the created process or -1 on error
// (in which case errno is set to an appropriate value)
_TEST_EXPORT rw_pid_t
rw_process_create (const char*, char* const []);
// result is a pointer to a buffer where the result code
// of the specified process will be stored, or NULL
// returns pid of the specified process or -1 on error
// (in which case errno is set to an appropriate value)
// Errors:
// ECHILD: no specified process exists
_TEST_EXPORT rw_pid_t
rw_waitpid (rw_pid_t, int*);
#endif // RW_PROCESS_H_INCLUDED
/************************************************************************
*
* process.cpp - definitions of testsuite process helpers
*
* $Id: process.cpp
*
************************************************************************
*
* Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave
* Software division. Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0. Unless required by
* applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under
* the License.
*
**************************************************************************/
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
#include <rw_process.h>
#include <stddef.h>
#include <stdarg.h> // for va_copy, va_list, ...
#include <stdlib.h> // for free(), exit()
#include <string.h> // for strchr()
#include <ctype.h> // for isspace()
#include <errno.h> // for errno
#include <driver.h> // for rw_note(), ...
#include <rw_printf.h> // for rw_fprintf()
/**************************************************************************/
_TEST_EXPORT int
rw_vasnprintf (char**, size_t*, const char*, va_list);
/**************************************************************************/
#if defined (_WIN32) || defined (_WIN64)
#include <process.h> // for spawnvp(), cwait()
#else
#include <sys/types.h>
#include <sys/wait.h> // for waitpid()
#include <unistd.h> // for fork(), execv(), access()
#endif
/**************************************************************************/
static int
_rw_vsystem (const char *cmd, va_list va)
{
RW_ASSERT (0 != cmd);
char buffer [256];
char *buf = buffer;
size_t bufsize = sizeof buffer;
rw_vasnprintf (&buf, &bufsize, cmd, va);
rw_note (0, "file:" __FILE__, __LINE__, "executing \"%s\"", buf);
// avoid using const in order to prevent gcc warning on Linux
// issued for WIFSIGNALED() et al: cast from `const int*' to
// `int*' discards qualifiers from pointer target type:
// see http://sourceware.org/bugzilla/show_bug.cgi?id=1392
/* const */ int ret = system (buf);
if (ret) {
#if !defined (_WIN32) && !defined (_WIN64)
if (-1 == ret) {
// system() failed, e.g., because fork() failed
rw_error (0, __FILE__, __LINE__,
"system (\"%s\") failed: errno = %{#m} (%{m})",
buf);
}
else if (WIFSIGNALED (ret)) {
// command exited with a signal
const int signo = WTERMSIG (ret);
rw_error (0, __FILE__, __LINE__,
"the command \"%s\" exited with signal %d (%{K})",
buf, signo, signo);
}
else {
// command exited with a non-zero status
const int status = WEXITSTATUS (ret);
rw_error (0, __FILE__, __LINE__,
"the command \"%s\" exited with status %d",
buf, status);
}
#else // if defined (_WIN32) || defined (_WIN64)
// FIXME: make this more descriptive
rw_error (0, __FILE__, __LINE__,
"the command \"%s\" failed with code %d",
buf, ret);
#endif // _WIN32
}
if (buf != buffer)
free (buf);
return ret;
}
/**************************************************************************/
_TEST_EXPORT int
rw_system (const char *cmd, ...)
{
va_list va;
va_start (va, cmd);
const int ret = _rw_vsystem (cmd, va);
va_end (va);
return ret;
}
/**************************************************************************/
// splits command line to the array of parameters
// note: modifies the cmd string
// returns the number of parameters in cmd
// if argv != 0 fills argv up to size elements
static size_t _rw_split_cmd (char* cmd, char** argv, size_t size)
{
RW_ASSERT (0 != cmd);
size_t ret = 0;
for (char* end = cmd + strlen (cmd); cmd != end; /*do nothing*/) {
// skip the leading spaces
while (isspace (*cmd))
++cmd;
if (end == cmd)
break;
if (argv && ret < size)
argv [ret] = cmd;
++ret;
if ('\'' == *cmd || '\"' == *cmd) {
char* const cmd1 = cmd + 1;
// search the closing quote
if (char* pos = strchr (cmd1, *cmd)) {
// found, remove the quotes
// remove the opening quote
memmove (cmd, cmd1, pos - cmd1);
// remove the closing quote
cmd = pos - 1;
memmove (cmd, pos + 1, end - pos);
end -= 2;
} else {
// not found
break;
}
}
// search the space
while (*cmd && !isspace (*cmd))
++cmd;
if (cmd != end)
// found, replace to '\0'
*cmd++ = '\0';
}
return ret;
}
/**************************************************************************/
static rw_pid_t
_rw_vprocess_create (const char* cmd, va_list va)
{
RW_ASSERT (0 != cmd);
char buffer [256];
char *buf = buffer;
size_t bufsize = sizeof (buffer);
rw_vasnprintf (&buf, &bufsize, cmd, va);
const size_t MAX_PARAMS = 63;
char* argv [MAX_PARAMS + 1] = { 0 };
int ret;
size_t argc = _rw_split_cmd (buf, argv, MAX_PARAMS);
if (0 < argc && MAX_PARAMS >= argc)
ret = rw_process_create (argv [0], argv);
else {
ret = -1;
errno = ENOMEM;
}
if (buf != buffer)
free (buf);
return ret;
}
/**************************************************************************/
_TEST_EXPORT rw_pid_t
rw_process_create (const char* cmd, ...)
{
va_list va;
va_start (va, cmd);
const int ret = _rw_vprocess_create (cmd, va);
va_end (va);
return ret;
}
/**************************************************************************/
_TEST_EXPORT rw_pid_t
rw_process_create (const char* path, char* const argv[])
{
#if defined (_WIN32) || defined (_WIN64)
const char* function = "spawnvp()";
rw_pid_t child_pid = spawnvp (P_NOWAIT, path, argv);
#else
rw_pid_t child_pid = -1;
const char* function = "access()";
const int res = access (path, X_OK);
if (0 == res) {
function = "fork()";
child_pid = fork ();
if (0 == child_pid) {
// the child process
execvp (path, argv);
// the execvp returns only if an error occurs
rw_fprintf (rw_stderr, "%s:%d execvp() failed: errno = %{#m}
(%{m})\n");
exit (1);
}
}
// the parent process
#endif // #if defined (_WIN32) || defined (_WIN64)
if (-1 == child_pid)
rw_error (0, __FILE__, __LINE__,
"%s failed: errno = %{#m} (%{m})",
function);
return child_pid;
}
/**************************************************************************/
_TEST_EXPORT rw_pid_t
rw_waitpid (rw_pid_t pid, int* result)
{
#if defined (_WIN32) || defined (_WIN64)
const char* function = "cwait()";
const rw_pid_t ret = cwait (result, pid, WAIT_CHILD);
#else
const char* function = "waitpid()";
int status = 0;
const rw_pid_t ret = waitpid (pid, &status, 0);
if (ret == pid) {
if (WIFSIGNALED (status)) {
// process exited with a signal
const int signo = WTERMSIG (status);
rw_error (0, __FILE__, __LINE__,
"the process (pid=%ld) exited with signal %d (%{K})",
long (ret), signo, signo);
if (result)
*result = signo;
}
else if (WIFEXITED (status)) {
// process exited with a status
const int retcode = WEXITSTATUS (status);
if (retcode)
rw_error (0, __FILE__, __LINE__,
"the process (pid=%ld) exited with return code %d",
long (ret), retcode);
if (result)
*result = retcode;
}
else if (result)
*result = -1;
}
#endif // #if defined (_WIN32) || defined (_WIN64)
if (-1 == ret) {
rw_error (0, __FILE__, __LINE__,
"%s failed: errno = %{#m} (%{m})",
function);
}
return ret;
}
/************************************************************************
*
* 0.process.cpp - test exercising the rw_process_create()
* and rw_waitpid() functions
*
* $Id: 0.process.cpp
*
************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
**************************************************************************/
#include <string.h> // for strcmp()
#include <errno.h> // for errno
#include <rw_process.h> // for rw_process_create(), rw_waitpid()
#include <driver.h> // for rw_test()
static int _rw_child = 0;
static char arg1[] = "--child=1";
static char arg2[] = "--no-stdout";
static char* args [] = { 0, arg1, arg2, 0 };
static const int nargs = sizeof (args) / sizeof (*args) - 1;
static int join_test (rw_pid_t pid)
{
rw_assert (-1 != pid, __FILE__, __LINE__,
"rw_process_create() failed, errno = %{#m}");
if (-1 == pid)
return 1;
int result = 0;
const rw_pid_t ret = rw_waitpid (pid, &result);
rw_assert (-1 != ret, __FILE__, __LINE__,
"rw_waitpid() failed, errno = %{#m}");
if (-1 == ret)
return 1;
rw_assert (0 == result, __FILE__, __LINE__,
"result: expected 0, got %d", result);
return result;
}
static int test1 ()
{
const rw_pid_t pid = rw_process_create (args [0], args);
rw_assert (-1 != pid, __FILE__, __LINE__,
"rw_process_create() failed, errno = %{#m}");
return -1 == pid ? 1 : join_test (pid);
}
static int test2 ()
{
const rw_pid_t pid = rw_process_create ("\"%s\" %s %s",
args[0], args[1], args[2]);
rw_assert (-1 != pid, __FILE__, __LINE__,
"rw_process_create() failed, errno = %{#m}");
return -1 == pid ? 1 : join_test (pid);
}
static int test3 ()
{
const rw_pid_t pid = rw_process_create ("/\\/\\/\\", args);
rw_assert (-1 == pid, __FILE__, __LINE__,
"rw_process_create returns %ld, expected -1",
long (pid));
if (-1 != pid) {
rw_waitpid (pid, 0);
return 1;
}
rw_assert (ENOENT == errno, __FILE__, __LINE__,
"errno: expected ENOENT, got %{#m}");
return ENOENT == errno ? 0 : 1;
}
static int test4 ()
{
const rw_pid_t pid = rw_waitpid (1234, 0);
rw_assert (-1 == pid, __FILE__, __LINE__,
"rw_waitpid returns %ld, expected -1",
long (pid));
if (-1 != pid)
return 1;
rw_assert (ECHILD == errno, __FILE__, __LINE__,
"errno: expected ECHILD, got %{#m}");
return ECHILD == errno ? 0 : 1;
}
static int
run_test (int argc, char** argv)
{
if (_rw_child) {
// compare number of parameters with expected
if (nargs != argc)
return nargs;
// compare the parameters with expected
for (int i = 1; i < argc; ++i) {
if (0 != strcmp (argv [i], args [i]))
return i;
}
return 0;
}
args [0] = argv [0];
int fails = 0;
if (test1 ())
++fails;
if (test2 ())
++fails;
if (test3 ())
++fails;
if (test4 ())
++fails;
return fails;
}
int main (int argc, char *argv[])
{
return rw_test (argc, argv, __FILE__,
"0.process",
"", run_test,
"|-child#0",
&_rw_child,
0 /*sentinel*/);
return 0;
}