> -----Original Message-----
> From: Martin Sebor [mailto:[EMAIL PROTECTED]
> Sent: Friday, July 28, 2006 4:02 AM
> To: [email protected]
> Subject: Re: testsuite process helpers (was: RE: string
> methods thread safety)
[...]

Martin, I have been updated the files rw_process.h, process.cpp and 0.process.cpp. The new files are attached.

  ChangeLog:
    * rw_process.h (rw_pid_t): The type long changed to intptr_t
    (on Windows) and pid_t (on other platforms).
    (rw_process_error_report_mode): New variable to enable/disable
    rw_error() outputs within defined functions.
    (rw_wait_pid): Added the timeout parameter.
    (rw_process_kill): New function to kill the specified process.
    * process.cpp: Ditto.
    * 0.process.cpp: New test exercising the rw_process_create(),
    rw_process_kill() and rw_waitpid() functions.


> I think it's important to exercise this functionality. We'll
> need to come up with a way to silence the errors in these
> tests. Maybe via some global variable or something like that.

  I added the extern variable to the rw_process.h to handle this.

// if == 0 - report about errors using rw_error() (default)
// if != 0 - do not report about errors
_TEST_EXPORT extern int
rw_process_error_report_mode;

Maybe will be better to implement a function rw_set_process_error_report_mode() which sets a new value and returns the previous one?


> There is another problem with the test: we cannot assume that
> 1234 (or any other number we pick out of a hat) is an invalid
> process id.

To resolve this I used the pid from succeeded test of the rw_process_create() instead of 1234.


> If there is a process with that pid our test might never
> finish (or fail with a false negative). Which brings up
> another point: we need to be able to kill the (child) process
> in case it hangs, and we need to exercise the ability to
> correctly report the signal the child process exited with, or
> more generally, its exit status.
To resolve this I added the additional parameter --timeout for the test (the default timeout is 5 seconds).


> Also, as a general comment on the test, it helps to give each
> test function a descriptive name (rather than test1, test2,
> etc. It's also nice to print out some information (using
> rw_info()) about what's being tested.

  Done.


Farid.
/************************************************************************
*
* 0.process.cpp - test exercising the rw_process_create(),
*                 rw_process_kill() 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 int _rw_timeout = 5;

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 rw_pid_t _rw_pid = -1;

static int join_test (rw_pid_t pid, bool should_hang)
{
    int result = 0;
    const rw_pid_t ret = rw_waitpid (pid, &result, _rw_timeout);

    rw_assert (-1 != ret, __FILE__, __LINE__,
               "rw_waitpid() failed, errno = %{#m}");

    if (-1 == ret)
        return 1;

    if (0 == ret) {
        // time_out elapsed, kill the process
        rw_process_kill (pid);

        // save error output mode
        const int prev_mode = rw_process_error_report_mode;
        // disable rw_error() within functions from rw_process.h
        rw_process_error_report_mode = 1;

        // remove the zombie process if present
        rw_waitpid (pid, 0);

        // restore error output mode
        rw_process_error_report_mode = prev_mode;

        rw_assert (should_hang, __FILE__, __LINE__,
                   "The child process unexpectedly deadlocked");

        return should_hang ? 0 : 1;
    }

    rw_assert (!should_hang, __FILE__, __LINE__,
               "Expected the deadlocked process, but process exited "
               "with code: %d",
               result);

    if (!should_hang)
        rw_assert (0 == result, __FILE__, __LINE__,
                   "Process exit code: expected 0, got %d", result);

    return result;
}

static int test_process_create1 ()
{
    rw_info (0, 0, 0,
             "Exercising the rw_process_create "
             "(const char*, char* const []) overload");

    const rw_pid_t pid = rw_process_create (args [0], args);

    rw_assert (-1 != pid, __FILE__, __LINE__,
               "rw_process_create() failed, errno = %{#m}");

    if (-1 == pid)
        return 1;

    // save the pid for test exercising rw_waitpid() fail
    if (-1 == _rw_pid)
        _rw_pid = pid;

    return join_test (pid, false);
}

static int test_process_create2 ()
{
    rw_info (0, 0, 0,
             "Exercising the rw_process_create (const char*, ...) overload");

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

    if (-1 == pid)
        return 1;

    // save the pid for test exercising rw_waitpid() fail
    if (-1 == _rw_pid)
        _rw_pid = pid;

    return join_test (pid, false);
}

static int test_process_deadlocked ()
{
    rw_info (0, 0, 0,
             "Exercising the rw_waitpid() with timeout and deadlocked process");

    const rw_pid_t pid = rw_process_create ("\"%s\" --child=2 %s",
                                            args[0], args[2]);

    rw_assert (-1 != pid, __FILE__, __LINE__,
               "rw_process_create() failed, errno = %{#m}");

    return -1 == pid ? 1 : join_test (pid, true);
}

static int test_process_create_fail ()
{
    rw_info (0, 0, 0,
             "Exercising the rw_process_create() behavior "
             "when invalid path specified");

    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 test_waitpid_fail ()
{
    if (-1 == _rw_pid) {
        rw_info (0, 0, 0,
                 "The test, exercising the rw_waitpid() behavior "
                 "when invalid pid specified is disabled");

        return 0;
    }

    rw_info (0, 0, 0,
             "Exercising the rw_waitpid() behavior "
             "when invalid pid specified");

    const rw_pid_t pid = rw_waitpid (_rw_pid, 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 test_process_kill_fail ()
{
    if (-1 == _rw_pid) {
        rw_info (0, 0, 0,
                 "The test, exercising the rw_process_kill() behavior "
                 "when invalid pid specified is disabled");

        return 0;
    }

    rw_info (0, 0, 0,
             "Exercising the rw_process_kill() behavior "
             "when invalid pid specified");

    const int res = rw_process_kill (_rw_pid);

    rw_assert (-1 == res, __FILE__, __LINE__,
               "rw_process_kill returns %ld, expected -1",
               long (res));

    if (-1 != res)
        return 1;

    rw_assert (ESRCH == errno, __FILE__, __LINE__,
               "errno: expected ESRCH, got %{#m}");

    return ESRCH == errno ? 0 : 1;
}

static int
run_test (int argc, char** argv)
{
    if (_rw_child) {

        if (2 == _rw_child) {
            // simulate the deadlock
            while (true) ;
        }

        // 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 (test_process_create1 ())
        ++fails;

    if (test_process_create2 ())
        ++fails;

    if (test_process_deadlocked ())
        ++fails;

    // disable rw_error() within functions from rw_process.h
    rw_process_error_report_mode = 1;

    if (test_process_create_fail ())
        ++fails;

    if (test_waitpid_fail ())
        ++fails;

    if (test_process_kill_fail ())
        ++fails;

    return fails;
}

int main (int argc, char *argv[])
{
    return rw_test (argc, argv, __FILE__,
                    "0.process",
                    "", run_test,
                    "|-child#0 "
                    "|-timeout#",
                    &_rw_child,
                    &_rw_timeout,
                    0 /*sentinel*/);
}
/************************************************************************
 *
 * process.cpp - definitions of testsuite process helpers
 *
 * $Id: 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.
 * 
 **************************************************************************/

// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC

#include <rw_process.h>

#include <stddef.h>       // for size_t
#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()

#ifndef ENOMEM
#  define ENOMEM 12       // e.g., Linux, Solaris
#endif

#ifdef __CYGWIN__
// use the Windows API on Cygwin
#  define _WIN32
#endif

/**************************************************************************/

_TEST_EXPORT int
rw_process_error_report_mode = 0;

#define RW_ERROR(arg)                      \
    if (0 == rw_process_error_report_mode) \
        rw_error arg

/**************************************************************************/

_TEST_EXPORT int
rw_vasnprintf (char**, size_t*, const char*, va_list);

/**************************************************************************/

#if defined (_WIN32) || defined (_WIN64)

#  include <process.h>      // for spawnvp(), cwait()
#  include <windows.h>      // for WaitForSingleObject(), ...

extern "C" void _dosmaperr(unsigned long);

#else

#  include <sys/types.h>
#  include <sys/wait.h>   // for waitpid()
#  include <unistd.h>     // for fork(), execv(), access()
#  include <setjmp.h>     // for setjmp, longjmp
#  include <signal.h>     // for signal

#endif   // _WIN{32,64}

/**************************************************************************/

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

    rw_pid_t 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 rw_pid_t 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 rw_pid_t child_pid = spawnvp (P_NOWAIT, path, argv);

    if (-1 == child_pid)
        RW_ERROR ((0, __FILE__, __LINE__,
                  "spawnp (P_NOWAIT, %#s, %{As}) failed: errno = %{#m} (%{m})",
                  path, argv));

#else

    rw_pid_t child_pid = -1;

    const int res = access (path, X_OK);

    if (0 == res) {

        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 (%#s, %{As}) failed: "
                        "errno = %{#m} (%{m})\n", path, argv);

            exit (1);
        }
        else if (-1 == child_pid)
            RW_ERROR ((0, __FILE__, __LINE__,
                      "fork () failed: errno = %{#m} (%{m})"));
    }
    else
        RW_ERROR ((0, __FILE__, __LINE__,
                  "access (%#s, X_OK) failed: errno = %{#m} (%{m})",
                  path));

#endif  // #if defined (_WIN32) || defined (_WIN64)

    return child_pid;
}

/**************************************************************************/

#if !defined (_WIN32) && !defined (_WIN64)

#ifdef sigsetjmp
#define SETJMP(env) sigsetjmp (env, 1)
#else
#define SETJMP(env) setjmp (env)
#endif

static jmp_buf mark;

extern "C" {

static void
sig_handler (int)
{
    longjmp (mark, -1);
}

}
#endif  // #if !defined (_WIN32) && !defined (_WIN64)


_TEST_EXPORT rw_pid_t
rw_waitpid (rw_pid_t pid, int* result, int timeout/* = -1*/)
{
#if defined (_WIN32) || defined (_WIN64)

    /* Explicitly check for process_id being -1 or -2. In Windows NT,
    * -1 is a handle on the current process, -2 is a handle on the
    * current thread, and it is perfectly legal to to wait (forever)
    * on either */
    if (-1 == pid || -2 == pid) {
        errno = ECHILD;
        return -1;
    }

    rw_pid_t ret = pid;

    /* wait for child process, then fetch its exit code */
    const HANDLE hProcess = HANDLE (pid);

    const DWORD dwMilliseconds = 0 > timeout ? INFINITE : DWORD (timeout * 
1000);

    const DWORD dwRet = WaitForSingleObject (hProcess, dwMilliseconds);

    DWORD dwErr = ERROR_SUCCESS;

    if (WAIT_OBJECT_0 == dwRet) {
        DWORD dwExitCode;
        if (GetExitCodeProcess (hProcess, &dwExitCode)) {

            CloseHandle (hProcess);

            if (dwExitCode)
                RW_ERROR ((0, __FILE__, __LINE__,
                          "the process (pid=%ld) exited with return code %d",
                          long (pid), int (dwExitCode)));

            if (result)
                *result = int (dwExitCode);
        }
        else {
            dwErr = GetLastError ();
            RW_ERROR ((0, __FILE__, __LINE__,
                      "GetExitCodeProcess (%li, %#p) failed: "
                      "GetLastError() = %zu",
                      pid, &dwExitCode, size_t (dwErr)));
        }
    }
    else if (WAIT_FAILED == dwRet) {
        dwErr = GetLastError ();
        RW_ERROR ((0, __FILE__, __LINE__,
                  "WaitForSingleObject (%li, %{?}%s%{:}%zu%{;}) failed: "
                  "GetLastError() = %zu",
                  pid, INFINITE == dwMilliseconds, "INFINITE",
                  size_t (dwMilliseconds), size_t (dwErr)));
    }
    else {
        // time-out elapsed
        RW_ASSERT (WAIT_TIMEOUT == dwRet);

        return 0;
    }

    if (ERROR_SUCCESS != dwErr) {
        ret = -1;

        if (ERROR_INVALID_HANDLE == dwErr)
            errno = ECHILD;
        else
            _dosmaperr (dwErr);
    }

#else   // #if !defined (_WIN32) || defined (_WIN64)

    void (* old_handler)(int);

    if (0 < timeout) {
        old_handler = signal (SIGALRM, sig_handler);
        alarm (timeout);
    }

    int status = 0;
    const int options = timeout ? 0 : WNOHANG;

    rw_pid_t ret = 0;

    if (0 >= timeout || 0 == SETJMP (mark))
        ret = waitpid (pid, &status, options);

    if (0 < timeout) {
        // restore the previous handler
        signal (SIGALRM, old_handler);

        if (0 == ret)
            return 0;   // the time-out interval elapsed
    }

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

    if (-1 == ret)
        RW_ERROR ((0, __FILE__, __LINE__,
                  "waitpid (%li, %#p, %i) failed: errno = %{#m} (%{m})",
                  pid, &status, options));

#endif  // #if defined (_WIN32) || defined (_WIN64)

    return ret;
}


_TEST_EXPORT int
rw_process_kill (rw_pid_t pid)
{
#if defined (_WIN32) || defined (_WIN64)

    int ret = 0;

    if (!TerminateProcess (HANDLE (pid), DWORD (-1))) {

        ret = -1;

        const DWORD dwErr = GetLastError ();

        if (ERROR_INVALID_HANDLE == dwErr)
            errno = ESRCH;
        else if (ERROR_ACCESS_DENIED == dwErr)
            errno = EPERM;
        else
            _dosmaperr (dwErr);

        RW_ERROR ((0, __FILE__, __LINE__,
                  "TerminateProcess (%li, -1) failed: GetLastError() = %zu",
                  pid, size_t (dwErr)));
    }

#else   // #if !defined (_WIN32) || defined (_WIN64)

    const int ret = kill (pid, SIGKILL);

    if (-1 == ret)
        RW_ERROR ((0, __FILE__, __LINE__,
                  "kill (%li, SIGKILL) failed: errno = %{#m} (%{m})",
                  pid));

#endif  // #if defined (_WIN32) || defined (_WIN64)

    return ret;
}
/************************************************************************
 *
 * rw_process.h - declarations of testsuite process helpers
 *
 * $Id: rw_process.h $
 *
 ************************************************************************
 *
 * 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.
 * 
 **************************************************************************/

#ifndef RW_PROCESS_H_INCLUDED
#define RW_PROCESS_H_INCLUDED


#include <testdefs.h>

#if defined (_WIN32) || defined (_WIN64) || defined (__CYGWIN__)

#include <stddef.h>     // for intptr_t
typedef intptr_t rw_pid_t;

#else

#include <sys/types.h>
typedef pid_t rw_pid_t;

#endif

// if == 0 - report about errors using rw_error() (default)
// if != 0 - do not report about errors
_TEST_EXPORT extern int
rw_process_error_report_mode;

_TEST_EXPORT int
rw_system (const char*, ...);

// 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* /*path*/, char* const /*argv*/[]);

// result is a pointer to a buffer where the result code
//   of the specified process will be stored, or NULL
// the function suspends execution of the current process
// until a child has exited or specified timeout is reached
// returns:
//   pid of the specified process
//   0 when process still active
//   -1 on error (in which case errno is set to an appropriate value)
// timeout is timeout interval in seconds.
//   if timeout < 0 the function returns if the interval elapses
//   if timeout == 0 the function returns immediately
//   if timeout < 0 the function's time-out interval never elapses
// Errors:
//   ECHILD: no specified process exists
_TEST_EXPORT rw_pid_t
rw_waitpid (rw_pid_t /*pid*/, int* /*result*/, int /*timeout*/ = -1);

// returns:
//  0 when process terminated successfully
//  -1 on error (in which case errno is set to an appropriate value)
// Errors:
//   ESRCH: the pid does not exist
//   EPERM: the calling process does not have permission
//          to terminate the specified process
_TEST_EXPORT int
rw_process_kill (rw_pid_t);

#endif   // RW_PROCESS_H_INCLUDED

Reply via email to