> -----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;
}

Reply via email to