Rudimentary process management.
Signed-off-by: Martin Bremmer <martin.bremmer@adlinktech.com>
This commit is contained in:
parent
d146716d1d
commit
0269774a60
9 changed files with 1378 additions and 33 deletions
|
@ -74,7 +74,6 @@ list(APPEND sources
|
|||
"${source_path}/io.c"
|
||||
"${source_path}/log.c"
|
||||
"${source_path}/retcode.c"
|
||||
"${source_path}/process.c"
|
||||
"${source_path}/strtod.c"
|
||||
"${source_path}/strtol.c")
|
||||
|
||||
|
@ -104,7 +103,7 @@ list(APPEND sources
|
|||
# network stack. In order to mix-and-match various compilers, architectures,
|
||||
# operating systems, etc input from the build system is required.
|
||||
foreach(feature atomics cdtors environ heap ifaddrs random rusage
|
||||
sockets string sync threads time md5)
|
||||
sockets string sync threads time md5 process)
|
||||
if(EXISTS "${include_path}/dds/ddsrt/${feature}.h")
|
||||
list(APPEND headers "${include_path}/dds/ddsrt/${feature}.h")
|
||||
file(GLOB
|
||||
|
|
|
@ -13,11 +13,9 @@
|
|||
#define DDSRT_PROCESS_H
|
||||
|
||||
#include "dds/export.h"
|
||||
#include "dds/ddsrt/time.h"
|
||||
#include "dds/ddsrt/types.h"
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "dds/ddsrt/retcode.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
typedef DWORD ddsrt_pid_t;
|
||||
|
@ -33,6 +31,11 @@ typedef pid_t ddsrt_pid_t;
|
|||
#endif
|
||||
#endif /* _WIN32 */
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return process ID (PID) of the calling process.
|
||||
*
|
||||
|
@ -41,6 +44,168 @@ typedef pid_t ddsrt_pid_t;
|
|||
DDS_EXPORT ddsrt_pid_t
|
||||
ddsrt_getpid(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create new process.
|
||||
*
|
||||
* Creates a new process using the provided executable file. It will have
|
||||
* default priority and scheduling.
|
||||
*
|
||||
* Process arguments are represented by argv, which can be null. If argv is
|
||||
* not null, then the array must be null terminated. The argv array only has
|
||||
* to contain the arguments, the executable filename doesn't have to be in
|
||||
* the first element, which is normally the convention.
|
||||
*
|
||||
* @param[in] executable Executable file name.
|
||||
* @param[in] argv Arguments array.
|
||||
* @param[out] pid ID of the created process.
|
||||
*
|
||||
* @returns A dds_retcode_t indicating success or failure.
|
||||
*
|
||||
* @retval DDS_RETCODE_OK
|
||||
* Process successfully created.
|
||||
* @retval DDS_RETCODE_BAD_PARAMETER
|
||||
* Provided file is not available or not executable.
|
||||
* @retval DDS_RETCODE_NOT_ALLOWED
|
||||
* Caller is not permitted to start the process.
|
||||
* @retval DDS_RETCODE_OUT_OF_RESOURCES
|
||||
* Not enough resources to start the process.
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* Process could not be created.
|
||||
*/
|
||||
DDS_EXPORT dds_retcode_t
|
||||
ddsrt_proc_create(
|
||||
const char *executable,
|
||||
char *const argv[],
|
||||
ddsrt_pid_t *pid);
|
||||
|
||||
/**
|
||||
* @brief Wait for a specific child process to have finished.
|
||||
*
|
||||
* This function takes a process id and will wait until the related process
|
||||
* has finished or the timeout is reached.
|
||||
*
|
||||
* If the process finished, then the exit code of that process will be copied
|
||||
* into the given 'code' argument.
|
||||
*
|
||||
* Internally, the timeout can be round-up to the nearest milliseconds or
|
||||
* seconds, depending on the platform.
|
||||
*
|
||||
* See ddsrt_proc_waitpids() for waiting on all child processes.
|
||||
*
|
||||
* @param[in] pid Process ID (PID) to get the exit code from.
|
||||
* @param[in] timemout Time within the process is expected to finish.
|
||||
* @param[out] code The exit code of the process.
|
||||
*
|
||||
* @returns A dds_retcode_t indicating success or failure.
|
||||
*
|
||||
* @retval DDS_RETCODE_OK
|
||||
* Process has terminated and its exit code has been captured.
|
||||
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
|
||||
* Process is still alive (only when timeout == 0).
|
||||
* @retval DDS_RETCODE_TIMEOUT
|
||||
* Process is still alive (even after the timeout).
|
||||
* @retval DDS_RETCODE_BAD_PARAMETER
|
||||
* Negative timeout.
|
||||
* @retval DDS_RETCODE_NOT_FOUND
|
||||
* Process unknown.
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* Getting the exit code failed for an unknown reason.
|
||||
*/
|
||||
DDS_EXPORT dds_retcode_t
|
||||
ddsrt_proc_waitpid(
|
||||
ddsrt_pid_t pid,
|
||||
dds_duration_t timeout,
|
||||
int32_t *code);
|
||||
|
||||
/**
|
||||
* @brief Wait for a random child process to have finished.
|
||||
*
|
||||
* This function will wait until anyone of the child processes has
|
||||
* finished or the timeout is reached.
|
||||
*
|
||||
* If a process finished, then the exit code of that process will be
|
||||
* copied into the given 'code' argument. The pid of the process will
|
||||
* be put in the 'pid' argument.
|
||||
*
|
||||
* Internally, the timeout can be round-up to the nearest milliseconds or
|
||||
* seconds, depending on the platform.
|
||||
*
|
||||
* See ddsrt_proc_waitpid() for waiting on a specific child process.
|
||||
*
|
||||
* @param[in] timemout Time within a process is expected to finish.
|
||||
* @param[out] pid Process ID (PID) of the finished process.
|
||||
* @param[out] code The exit code of the process.
|
||||
*
|
||||
* @returns A dds_retcode_t indicating success or failure.
|
||||
*
|
||||
* @retval DDS_RETCODE_OK
|
||||
* A process has terminated.
|
||||
* Its exit code and pid have been captured.
|
||||
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
|
||||
* All child processes are still alive (only when timeout == 0).
|
||||
* @retval DDS_RETCODE_TIMEOUT
|
||||
* All child processes are still alive (even after the timeout).
|
||||
* @retval DDS_RETCODE_BAD_PARAMETER
|
||||
* Negative timeout.
|
||||
* @retval DDS_RETCODE_NOT_FOUND
|
||||
* There are no processes to wait for.
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* Getting the exit code failed for an unknown reason.
|
||||
*/
|
||||
DDS_EXPORT dds_retcode_t
|
||||
ddsrt_proc_waitpids(
|
||||
dds_duration_t timeout,
|
||||
ddsrt_pid_t *pid,
|
||||
int32_t *code);
|
||||
|
||||
/**
|
||||
* @brief Checks if a process exists.
|
||||
*
|
||||
* @param[in] pid Process ID (PID) to check if it exists.
|
||||
*
|
||||
* @returns A dds_retcode_t indicating success or failure.
|
||||
*
|
||||
* @retval DDS_RETCODE_OK
|
||||
* The process exists.
|
||||
* @retval DDS_RETCODE_NOT_FOUND
|
||||
* The process does not exist.
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* Determining if a process exists or not, failed.
|
||||
*/
|
||||
DDS_EXPORT dds_retcode_t
|
||||
ddsrt_proc_exists(
|
||||
ddsrt_pid_t pid);
|
||||
|
||||
/**
|
||||
* @brief Kill a process.
|
||||
*
|
||||
* This function will try to forcefully kill the process (identified
|
||||
* by pid).
|
||||
*
|
||||
* When DDS_RETCODE_OK is returned, it doesn't mean that the process
|
||||
* was actually killed. It only indicates that the process was
|
||||
* 'told' to terminate. Call ddsrt_proc_exists() to know
|
||||
* for sure if the process was killed.
|
||||
*
|
||||
* @param[in] pid Process ID (PID) of the process to terminate.
|
||||
*
|
||||
* @returns A dds_retcode_t indicating success or failure.
|
||||
*
|
||||
* @retval DDS_RETCODE_OK
|
||||
* Kill attempt has been started.
|
||||
* @retval DDS_RETCODE_BAD_PARAMETER
|
||||
* Process unknown.
|
||||
* @retval DDS_RETCODE_ILLEGAL_OPERATION
|
||||
* Caller is not allowed to kill the process.
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* Kill failed for an unknown reason.
|
||||
*/
|
||||
DDS_EXPORT dds_retcode_t
|
||||
ddsrt_proc_kill(
|
||||
ddsrt_pid_t pid);
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include "dds/ddsrt/process.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
ddsrt_pid_t
|
||||
ddsrt_getpid(void)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return GetCurrentProcessId();
|
||||
#else
|
||||
/* Mapped to taskIdSelf() in VxWorks kernel mode. */
|
||||
return getpid();
|
||||
#endif
|
||||
}
|
303
src/ddsrt/src/process/posix/process.c
Normal file
303
src/ddsrt/src/process/posix/process.c
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include "dds/ddsrt/process.h"
|
||||
#include "dds/ddsrt/string.h"
|
||||
#include "dds/ddsrt/heap.h"
|
||||
|
||||
|
||||
ddsrt_pid_t
|
||||
ddsrt_getpid(void)
|
||||
{
|
||||
/* Mapped to taskIdSelf() in VxWorks kernel mode. */
|
||||
return getpid();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This'll take a argv and prefixes it with the given prefix.
|
||||
* If argv is NULL, the new argv array will only contain the prefix and a NULL.
|
||||
* The result array is always terminated with NULL.
|
||||
*/
|
||||
static char**
|
||||
prefix_argv(const char *prefix, char *const argv_in[])
|
||||
{
|
||||
char **argv_out;
|
||||
size_t argc = 0;
|
||||
|
||||
assert(prefix);
|
||||
|
||||
if (argv_in != NULL) {
|
||||
while (argv_in[argc] != NULL) {
|
||||
argc++;
|
||||
}
|
||||
}
|
||||
|
||||
argv_out = ddsrt_calloc((argc + 2), sizeof(char*));
|
||||
if (argv_out) {
|
||||
size_t argi;
|
||||
argv_out[0] = (char*)prefix;
|
||||
for (argi = 0; argi < argc; argi++) {
|
||||
argv_out[argi + 1] = (char*)argv_in[argi];
|
||||
}
|
||||
argv_out[argc + 1] = NULL;
|
||||
}
|
||||
|
||||
return argv_out;
|
||||
}
|
||||
|
||||
|
||||
static void no_op(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
}
|
||||
|
||||
|
||||
static dds_retcode_t
|
||||
waitpids(
|
||||
ddsrt_pid_t request_pid,
|
||||
dds_duration_t timeout,
|
||||
ddsrt_pid_t *child_pid,
|
||||
int32_t *code)
|
||||
{
|
||||
struct sigaction sigactold;
|
||||
struct sigaction sigact;
|
||||
dds_retcode_t rv;
|
||||
int options = 0;
|
||||
int ret;
|
||||
int s;
|
||||
|
||||
if (timeout < 0) {
|
||||
return DDS_RETCODE_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
options = WNOHANG;
|
||||
} else if (timeout != DDS_INFINITY) {
|
||||
/* Round-up timemout to alarm seconds. */
|
||||
unsigned secs;
|
||||
secs = (unsigned)(timeout / DDS_NSECS_IN_SEC);
|
||||
if ((timeout % DDS_NSECS_IN_SEC) != 0) {
|
||||
secs++;
|
||||
}
|
||||
/* Be sure that the SIGALRM only wakes up waitpid. */
|
||||
sigemptyset (&sigact.sa_mask);
|
||||
sigact.sa_handler = no_op;
|
||||
sigact.sa_flags = 0;
|
||||
sigaction (SIGALRM, &sigact, &sigactold);
|
||||
/* Now, set the alarm. */
|
||||
alarm(secs);
|
||||
}
|
||||
|
||||
ret = waitpid(request_pid, &s, options);
|
||||
if (ret > 0) {
|
||||
if (code) {
|
||||
if (WIFEXITED(s)) {
|
||||
*code = WEXITSTATUS(s);
|
||||
} else {
|
||||
*code = 1;
|
||||
}
|
||||
}
|
||||
if (child_pid) {
|
||||
*child_pid = ret;
|
||||
}
|
||||
rv = DDS_RETCODE_OK;
|
||||
} else if (ret == 0) {
|
||||
/* Process is still alive. */
|
||||
rv = DDS_RETCODE_PRECONDITION_NOT_MET;
|
||||
} else if ((ret == -1) && (errno == EINTR)) {
|
||||
/* Interrupted,
|
||||
* so process(es) likely didn't change state and are/is alive. */
|
||||
rv = DDS_RETCODE_TIMEOUT;
|
||||
} else if ((ret == -1) && (errno == ECHILD)) {
|
||||
/* Unknown pid. */
|
||||
rv = DDS_RETCODE_NOT_FOUND;
|
||||
} else {
|
||||
/* Unknown error. */
|
||||
rv = DDS_RETCODE_ERROR;
|
||||
}
|
||||
|
||||
if ((timeout != 0) && (timeout != DDS_INFINITY)) {
|
||||
/* Clean the alarm. */
|
||||
alarm(0);
|
||||
/* Reset SIGALRM actions. */
|
||||
sigaction(SIGALRM, &sigactold, NULL);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_create(
|
||||
const char *executable,
|
||||
char *const argv[],
|
||||
ddsrt_pid_t *pid)
|
||||
{
|
||||
dds_retcode_t rv;
|
||||
char **exec_argv;
|
||||
int exec_fds[2];
|
||||
int exec_err;
|
||||
pid_t spawn;
|
||||
ssize_t nr;
|
||||
|
||||
assert(executable != NULL);
|
||||
assert(pid != NULL);
|
||||
|
||||
/* Prefix the argv with the executable, which is the convention. */
|
||||
exec_argv = prefix_argv(executable, argv);
|
||||
if (exec_argv == NULL) {
|
||||
return DDS_RETCODE_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
/* Prepare pipe to know the result of the exec. */
|
||||
if (pipe(exec_fds) == -1) {
|
||||
rv = DDS_RETCODE_OUT_OF_RESOURCES;
|
||||
goto fail_pipe;
|
||||
}
|
||||
if ((fcntl(exec_fds[0], F_SETFD, fcntl(exec_fds[0], F_GETFD) | FD_CLOEXEC) == -1) ||
|
||||
(fcntl(exec_fds[1], F_SETFD, fcntl(exec_fds[1], F_GETFD) | FD_CLOEXEC) == -1) ){
|
||||
rv = DDS_RETCODE_ERROR;
|
||||
goto fail_fctl;
|
||||
}
|
||||
|
||||
/* Create a new process. */
|
||||
spawn = fork();
|
||||
if (spawn == -1)
|
||||
{
|
||||
rv = DDS_RETCODE_ERROR;
|
||||
goto fail_fork;
|
||||
}
|
||||
else if (spawn == 0)
|
||||
{
|
||||
/* Child process */
|
||||
|
||||
/* Run the executable, replacing current process. */
|
||||
execv(executable, exec_argv);
|
||||
|
||||
/* If executing this, something has gone wrong */
|
||||
exec_err = errno;
|
||||
(void)write(exec_fds[1], &exec_err, sizeof(int));
|
||||
close(exec_fds[1]);
|
||||
close(exec_fds[0]);
|
||||
ddsrt_free(exec_argv);
|
||||
_exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Parent process */
|
||||
|
||||
/* Get execv result. */
|
||||
rv = DDS_RETCODE_ERROR;
|
||||
close(exec_fds[1]);
|
||||
nr = read(exec_fds[0], &exec_err, sizeof(int));
|
||||
if (nr == 0) {
|
||||
/* Pipe closed by successful execv. */
|
||||
rv = DDS_RETCODE_OK;
|
||||
} else if (nr == sizeof(int)) {
|
||||
/* Translate execv error. */
|
||||
if ((exec_err == ENOENT ) ||
|
||||
(exec_err == ENOEXEC) ){
|
||||
rv = DDS_RETCODE_BAD_PARAMETER;
|
||||
} else if (exec_err == EACCES) {
|
||||
rv = DDS_RETCODE_NOT_ALLOWED;
|
||||
}
|
||||
}
|
||||
close(exec_fds[0]);
|
||||
|
||||
if (rv == DDS_RETCODE_OK) {
|
||||
/* Remember child pid. */
|
||||
*pid = spawn;
|
||||
} else {
|
||||
/* Remove the failed fork pid from the system list. */
|
||||
waitpid(spawn, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ddsrt_free(exec_argv);
|
||||
return rv;
|
||||
|
||||
fail_fork:
|
||||
fail_fctl:
|
||||
close(exec_fds[0]);
|
||||
close(exec_fds[1]);
|
||||
fail_pipe:
|
||||
ddsrt_free(exec_argv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_waitpid(
|
||||
ddsrt_pid_t pid,
|
||||
dds_duration_t timeout,
|
||||
int32_t *code)
|
||||
{
|
||||
if (pid > 0) {
|
||||
return waitpids(pid, timeout, NULL, code);
|
||||
}
|
||||
return DDS_RETCODE_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_waitpids(
|
||||
dds_duration_t timeout,
|
||||
ddsrt_pid_t *pid,
|
||||
int32_t *code)
|
||||
{
|
||||
return waitpids(0, timeout, pid, code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_exists(
|
||||
ddsrt_pid_t pid)
|
||||
{
|
||||
if (kill(pid, 0) == 0)
|
||||
return DDS_RETCODE_OK;
|
||||
else if (errno == EPERM)
|
||||
return DDS_RETCODE_OK;
|
||||
else if (errno == ESRCH)
|
||||
return DDS_RETCODE_NOT_FOUND;
|
||||
else
|
||||
return DDS_RETCODE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_kill(
|
||||
ddsrt_pid_t pid)
|
||||
{
|
||||
if (kill(pid, SIGKILL) == 0)
|
||||
return DDS_RETCODE_OK;
|
||||
else if (errno == EPERM)
|
||||
return DDS_RETCODE_ILLEGAL_OPERATION;
|
||||
else if (errno == ESRCH)
|
||||
return DDS_RETCODE_BAD_PARAMETER;
|
||||
else
|
||||
return DDS_RETCODE_ERROR;
|
||||
}
|
447
src/ddsrt/src/process/windows/process.c
Normal file
447
src/ddsrt/src/process/windows/process.c
Normal file
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <process.h>
|
||||
#include "dds/ddsrt/heap.h"
|
||||
#include "dds/ddsrt/time.h"
|
||||
#include "dds/ddsrt/string.h"
|
||||
#include "dds/ddsrt/atomics.h"
|
||||
#include "dds/ddsrt/process.h"
|
||||
#include "dds/ddsrt/timeconv.h"
|
||||
|
||||
|
||||
ddsrt_pid_t
|
||||
ddsrt_getpid(void)
|
||||
{
|
||||
return GetCurrentProcessId();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static HANDLE pid_to_phdl (ddsrt_pid_t pid);
|
||||
static dds_retcode_t process_get_exit_code(HANDLE phdl, int32_t *code);
|
||||
static dds_retcode_t process_kill (HANDLE phdl);
|
||||
static char* commandline (const char *exe, char *const argv_in[]);
|
||||
static BOOL child_add (HANDLE phdl);
|
||||
static void child_remove (HANDLE phdl);
|
||||
static DWORD child_list (HANDLE *list, DWORD max);
|
||||
static HANDLE child_handle (ddsrt_pid_t pid);
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_create(
|
||||
const char *executable,
|
||||
char *const argv[],
|
||||
ddsrt_pid_t *pid)
|
||||
{
|
||||
dds_retcode_t rv = DDS_RETCODE_ERROR;
|
||||
PROCESS_INFORMATION process_info;
|
||||
STARTUPINFO si;
|
||||
char *cmd;
|
||||
LPTCH environment;
|
||||
|
||||
assert(executable != NULL);
|
||||
assert(pid != NULL);
|
||||
|
||||
cmd = commandline(executable, argv);
|
||||
if (cmd == NULL) {
|
||||
return DDS_RETCODE_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
memset(&si, 0, sizeof(STARTUPINFO));
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
|
||||
/* The new process will inherit the input/output handles. */
|
||||
/* TODO: Redirect is not working yet. */
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
/* Get the environment variables to pass along. */
|
||||
environment = GetEnvironmentStrings();
|
||||
if(environment){
|
||||
BOOL created;
|
||||
created = CreateProcess(executable, // ApplicationName
|
||||
cmd, // CommandLine
|
||||
NULL, // ProcessAttributes
|
||||
NULL, // ThreadAttributes
|
||||
TRUE, // InheritHandles
|
||||
CREATE_NO_WINDOW, // dwCreationFlags
|
||||
(LPVOID)environment, // Environment
|
||||
NULL, // CurrentDirectory
|
||||
&si, // StartupInfo
|
||||
&process_info); // ProcessInformation
|
||||
if (created) {
|
||||
if (child_add(process_info.hProcess)) {
|
||||
*pid = process_info.dwProcessId;
|
||||
rv = DDS_RETCODE_OK;
|
||||
} else {
|
||||
process_kill(process_info.hProcess);
|
||||
rv = DDS_RETCODE_OUT_OF_RESOURCES;
|
||||
}
|
||||
} else {
|
||||
DWORD error = GetLastError();
|
||||
if ((ERROR_FILE_NOT_FOUND == error) ||
|
||||
(ERROR_PATH_NOT_FOUND == error)) {
|
||||
rv = DDS_RETCODE_BAD_PARAMETER;
|
||||
} else if (ERROR_ACCESS_DENIED == error) {
|
||||
rv = DDS_RETCODE_NOT_ALLOWED;
|
||||
}
|
||||
}
|
||||
FreeEnvironmentStrings(environment);
|
||||
}
|
||||
|
||||
ddsrt_free(cmd);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_waitpid(
|
||||
ddsrt_pid_t pid,
|
||||
dds_duration_t timeout,
|
||||
int32_t *code)
|
||||
{
|
||||
dds_retcode_t rv = DDS_RETCODE_OK;
|
||||
HANDLE phdl;
|
||||
DWORD ret;
|
||||
|
||||
if (timeout < 0) {
|
||||
return DDS_RETCODE_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
phdl = child_handle(pid);
|
||||
if (phdl == 0) {
|
||||
return DDS_RETCODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
ret = WaitForSingleObject(phdl, ddsrt_duration_to_msecs_ceil(timeout));
|
||||
if (ret != WAIT_OBJECT_0) {
|
||||
if (ret == WAIT_TIMEOUT) {
|
||||
rv = DDS_RETCODE_TIMEOUT;
|
||||
} else {
|
||||
rv = DDS_RETCODE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == DDS_RETCODE_OK) {
|
||||
rv = process_get_exit_code(phdl, code);
|
||||
}
|
||||
|
||||
if (rv == DDS_RETCODE_OK) {
|
||||
child_remove(phdl);
|
||||
}
|
||||
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_waitpids(
|
||||
dds_duration_t timeout,
|
||||
ddsrt_pid_t *pid,
|
||||
int32_t *code)
|
||||
{
|
||||
dds_retcode_t rv = DDS_RETCODE_OK;
|
||||
HANDLE hdls[MAXIMUM_WAIT_OBJECTS];
|
||||
HANDLE phdl;
|
||||
DWORD cnt;
|
||||
DWORD ret;
|
||||
|
||||
if (timeout < 0) {
|
||||
return DDS_RETCODE_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
cnt = child_list(hdls, MAXIMUM_WAIT_OBJECTS);
|
||||
if (cnt == 0) {
|
||||
return DDS_RETCODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
ret = WaitForMultipleObjects(cnt, hdls, FALSE, ddsrt_duration_to_msecs_ceil(timeout));
|
||||
if ((ret < WAIT_OBJECT_0) || (ret >= (WAIT_OBJECT_0 + cnt))) {
|
||||
if (ret == WAIT_TIMEOUT) {
|
||||
if (timeout == 0) {
|
||||
rv = DDS_RETCODE_PRECONDITION_NOT_MET;
|
||||
} else {
|
||||
rv = DDS_RETCODE_TIMEOUT;
|
||||
}
|
||||
} else {
|
||||
rv = DDS_RETCODE_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* Get the handle of the specific child that was triggered. */
|
||||
phdl = hdls[ret - WAIT_OBJECT_0];
|
||||
}
|
||||
|
||||
if (rv == DDS_RETCODE_OK) {
|
||||
rv = process_get_exit_code(phdl, code);
|
||||
}
|
||||
|
||||
if (rv == DDS_RETCODE_OK) {
|
||||
if (pid) {
|
||||
*pid = GetProcessId(phdl);
|
||||
}
|
||||
child_remove(phdl);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_exists(
|
||||
ddsrt_pid_t pid)
|
||||
{
|
||||
dds_retcode_t rv = DDS_RETCODE_NOT_FOUND;
|
||||
HANDLE phdl;
|
||||
|
||||
phdl = pid_to_phdl(pid);
|
||||
if (phdl != 0) {
|
||||
rv = process_get_exit_code(phdl, NULL);
|
||||
if (rv == DDS_RETCODE_PRECONDITION_NOT_MET) {
|
||||
/* Process still exists. */
|
||||
rv = DDS_RETCODE_OK;
|
||||
} else if (rv == DDS_RETCODE_OK) {
|
||||
/* The process has gone. */
|
||||
rv = DDS_RETCODE_NOT_FOUND;
|
||||
} else {
|
||||
rv = DDS_RETCODE_ERROR;
|
||||
}
|
||||
CloseHandle(phdl);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds_retcode_t
|
||||
ddsrt_proc_kill(
|
||||
ddsrt_pid_t pid)
|
||||
{
|
||||
dds_retcode_t rv = DDS_RETCODE_BAD_PARAMETER;
|
||||
HANDLE phdl;
|
||||
|
||||
phdl = pid_to_phdl(pid);
|
||||
if (phdl != 0) {
|
||||
/* Forcefully kill. */
|
||||
rv = process_kill(phdl);
|
||||
CloseHandle(phdl);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static HANDLE
|
||||
pid_to_phdl(ddsrt_pid_t pid)
|
||||
{
|
||||
return OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static dds_retcode_t
|
||||
process_get_exit_code(
|
||||
HANDLE phdl,
|
||||
int32_t *code)
|
||||
{
|
||||
dds_retcode_t rv = DDS_RETCODE_ERROR;
|
||||
DWORD tr;
|
||||
|
||||
assert(phdl != 0);
|
||||
|
||||
if (GetExitCodeProcess(phdl, &tr)) {
|
||||
if (tr == STILL_ACTIVE) {
|
||||
rv = DDS_RETCODE_PRECONDITION_NOT_MET;
|
||||
} else {
|
||||
if (code) {
|
||||
*code = (int32_t)tr;
|
||||
}
|
||||
rv = DDS_RETCODE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Forcefully kill the given process. */
|
||||
static dds_retcode_t
|
||||
process_kill(HANDLE phdl)
|
||||
{
|
||||
assert(phdl != 0);
|
||||
if (TerminateProcess(phdl, 1 /* exit code */) != 0) {
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
return DDS_RETCODE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Add quotes to a given string, escape it and add it to a buffer. */
|
||||
static char*
|
||||
insert_char(char *buf, size_t *len, size_t *max, char c)
|
||||
{
|
||||
static const size_t buf_inc = 64;
|
||||
if (*len == *max) {
|
||||
*max += buf_inc;
|
||||
buf = ddsrt_realloc(buf, *max);
|
||||
}
|
||||
if (buf) {
|
||||
buf[(*len)++] = c;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
static char*
|
||||
stringify_cat(char *buf, size_t *len, size_t *max, const char *str)
|
||||
{
|
||||
char last = '\0';
|
||||
|
||||
/* Start stringification with an opening double-quote. */
|
||||
buf = insert_char(buf, len, max, '\"');
|
||||
if (!buf) goto end;
|
||||
|
||||
/* Copy and escape the string. */
|
||||
while ((*str) != '\0') {
|
||||
if (*str == '\"') {
|
||||
buf = insert_char(buf, len, max, '\\');
|
||||
if (!buf) goto end;
|
||||
}
|
||||
buf = insert_char(buf, len, max, *str);
|
||||
if (!buf) goto end;
|
||||
last = *str;
|
||||
str++;
|
||||
}
|
||||
|
||||
/* For some reason, only the last backslash will be stripped.
|
||||
* No need to escape the other backslashes. */
|
||||
if (last == '\\') {
|
||||
buf = insert_char(buf, len, max, '\\');
|
||||
if (!buf) goto end;
|
||||
}
|
||||
|
||||
/* End stringification. */
|
||||
buf = insert_char(buf, len, max, '\"');
|
||||
if (!buf) goto end;
|
||||
buf = insert_char(buf, len, max, ' ');
|
||||
|
||||
end:
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create command line with executable and arguments. */
|
||||
static char*
|
||||
commandline(const char *exe, char *const argv_in[])
|
||||
{
|
||||
char *cmd = NULL;
|
||||
size_t len = 0;
|
||||
size_t max = 0;
|
||||
size_t argi;
|
||||
|
||||
assert(exe);
|
||||
|
||||
/* Add quoted and escaped executable. */
|
||||
cmd = stringify_cat(cmd, &len, &max, exe);
|
||||
if (!cmd) goto end;
|
||||
|
||||
/* Add quoted and escaped arguments. */
|
||||
if (argv_in != NULL) {
|
||||
for (argi = 0; argv_in[argi] != NULL; argi++) {
|
||||
cmd = stringify_cat(cmd, &len, &max, argv_in[argi]);
|
||||
if (!cmd) goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Terminate command line string. */
|
||||
cmd = insert_char(cmd, &len, &max, '\0');
|
||||
|
||||
end:
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
/* Maintain a list of children to be able to wait for all them. */
|
||||
static ddsrt_atomic_voidp_t g_children[MAXIMUM_WAIT_OBJECTS] = {0};
|
||||
|
||||
static BOOL
|
||||
child_update(HANDLE old, HANDLE new)
|
||||
{
|
||||
BOOL updated = FALSE;
|
||||
for (int i = 0; (i < MAXIMUM_WAIT_OBJECTS) && (!updated); i++)
|
||||
{
|
||||
updated = ddsrt_atomic_casvoidp(&(g_children[i]), old, new);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
child_add(HANDLE phdl)
|
||||
{
|
||||
return child_update(0, phdl);
|
||||
}
|
||||
|
||||
static void
|
||||
child_remove(HANDLE phdl)
|
||||
{
|
||||
(void)child_update(phdl, 0);
|
||||
}
|
||||
|
||||
static DWORD
|
||||
child_list(HANDLE *list, DWORD max)
|
||||
{
|
||||
HANDLE hdl;
|
||||
int cnt = 0;
|
||||
assert(list);
|
||||
assert(max <= MAXIMUM_WAIT_OBJECTS);
|
||||
for (int i = 0; (i < MAXIMUM_WAIT_OBJECTS); i++)
|
||||
{
|
||||
hdl = ddsrt_atomic_ldvoidp(&(g_children[i]));
|
||||
if (hdl != 0) {
|
||||
list[cnt++] = hdl;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static HANDLE
|
||||
child_handle(ddsrt_pid_t pid)
|
||||
{
|
||||
HANDLE phdl = 0;
|
||||
|
||||
for (int i = 0; (i < MAXIMUM_WAIT_OBJECTS) && (phdl == 0); i++)
|
||||
{
|
||||
phdl = ddsrt_atomic_ldvoidp(&(g_children[i]));
|
||||
if (phdl != 0) {
|
||||
if (GetProcessId(phdl) != pid) {
|
||||
phdl = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return phdl;
|
||||
}
|
|
@ -25,6 +25,7 @@ set(sources
|
|||
"random.c"
|
||||
"strlcpy.c"
|
||||
"socket.c"
|
||||
"process.c"
|
||||
"select.c")
|
||||
|
||||
add_cunit_executable(cunit_ddsrt ${sources})
|
||||
|
@ -42,3 +43,26 @@ endif()
|
|||
|
||||
target_include_directories(
|
||||
cunit_ddsrt PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
|
||||
# Create a separate test application that will be used to
|
||||
# test process management.
|
||||
add_executable(process_app process_app.c)
|
||||
target_link_libraries(process_app PRIVATE ddsrt)
|
||||
target_include_directories(
|
||||
process_app
|
||||
PRIVATE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
# Force the app to be at the same location, no matter what platform or build type.
|
||||
set_target_properties(
|
||||
process_app
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
# Let the cunit application know the location and name of the test application.
|
||||
set(process_app_name "${CMAKE_CURRENT_BINARY_DIR}/process_app${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
configure_file(
|
||||
"process_test.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/process_test.h" @ONLY)
|
||||
|
||||
|
|
284
src/ddsrt/tests/process.c
Normal file
284
src/ddsrt/tests/process.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Copyright(c) 2019 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "CUnit/Test.h"
|
||||
#include "process_test.h"
|
||||
#include "dds/ddsrt/time.h"
|
||||
#include "dds/ddsrt/strtol.h"
|
||||
#include "dds/ddsrt/environ.h"
|
||||
#include "dds/ddsrt/process.h"
|
||||
|
||||
|
||||
/*
|
||||
* Create a process that is expected to exit quickly.
|
||||
* Compare the exit code with the expected exit code.
|
||||
*/
|
||||
static void create_and_test_exit(const char *arg, int code)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
int32_t status;
|
||||
char *argv[] = { NULL, NULL };
|
||||
|
||||
argv[0] = (char*)arg;
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Check result. */
|
||||
CU_ASSERT_EQUAL(status, code);
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (ret != DDS_RETCODE_OK) {
|
||||
ddsrt_proc_kill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to create a process without arguments.
|
||||
* The exit status of the process should be PROCESS_DONE_NOTHING_EXIT_CODE.
|
||||
*/
|
||||
CU_Test(ddsrt_process, create)
|
||||
{
|
||||
create_and_test_exit(NULL, TEST_CREATE_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process that'll sleep for a while.
|
||||
* Try to kill that process.
|
||||
*/
|
||||
CU_Test(ddsrt_process, kill)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
/* Sleep for 20 seconds. It should be killed before then. */
|
||||
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
|
||||
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
/* Check if process is running. */
|
||||
ret = ddsrt_proc_exists(pid);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Kill it. */
|
||||
ret = ddsrt_proc_kill(pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Check if process is actually gone. */
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
ret = ddsrt_proc_exists(pid);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process that'll return it's own pid value (reduced
|
||||
* to fit the exit code range). It should match the pid that was
|
||||
* returned by the process create (also reduced to be able to
|
||||
* match the returned semi-pid value).
|
||||
*/
|
||||
CU_Test(ddsrt_process, pid)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
int32_t status;
|
||||
char *argv[] = { TEST_PID_ARG, NULL };
|
||||
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Compare the pid values. */
|
||||
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid));
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (ret != DDS_RETCODE_OK) {
|
||||
ddsrt_proc_kill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set a environment variable in the parent process.
|
||||
* Create a process that should have access to that env var.
|
||||
*/
|
||||
CU_Test(ddsrt_process, env)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
|
||||
ret = ddsrt_setenv(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
create_and_test_exit(TEST_ENV_ARG, TEST_ENV_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to create a process with an non-existing executable file.
|
||||
* It should fail.
|
||||
*/
|
||||
CU_Test(ddsrt_process, invalid)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
ret = ddsrt_proc_create("ProbablyNotAnValidExecutable", NULL, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_BAD_PARAMETER);
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (ret == DDS_RETCODE_OK) {
|
||||
ddsrt_proc_kill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process with a backslash in the argument
|
||||
*/
|
||||
CU_Test(ddsrt_process, arg_bslash)
|
||||
{
|
||||
create_and_test_exit(TEST_BSLASH_ARG, TEST_BSLASH_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process with a double-quote in the argument
|
||||
*/
|
||||
CU_Test(ddsrt_process, arg_dquote)
|
||||
{
|
||||
create_and_test_exit(TEST_DQUOTE_ARG, TEST_DQUOTE_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create two processes and wait for them simultaneously.
|
||||
*/
|
||||
CU_Test(ddsrt_process, waitpids)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t child;
|
||||
ddsrt_pid_t pid1 = 0;
|
||||
ddsrt_pid_t pid2 = 0;
|
||||
int32_t status;
|
||||
|
||||
/* Use retpid option to identify return values. */
|
||||
char *argv[] = { TEST_PID_ARG, NULL };
|
||||
|
||||
/* Start two processes. */
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid1);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid2);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Wait for both processes to have finished. */
|
||||
while (((pid1 != 0) || (pid2 != 0)) && (ret == DDS_RETCODE_OK)) {
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(10), &child, &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
if (child == pid1) {
|
||||
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid1));
|
||||
pid1 = 0;
|
||||
} else if (child == pid2) {
|
||||
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid2));
|
||||
pid2 = 0;
|
||||
} else {
|
||||
CU_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(10), &child, &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_NOT_FOUND);
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (pid1 != 0) {
|
||||
ddsrt_proc_kill(pid1);
|
||||
}
|
||||
if (pid2 != 0) {
|
||||
ddsrt_proc_kill(pid2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a sleeping process. Waiting for it should timeout.
|
||||
*/
|
||||
CU_Test(ddsrt_process, waitpid_timeout)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
/* Sleep for 20 seconds. We should have a timeout before then. */
|
||||
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
/* Timeout 0 should return DDS_RETCODE_PRECONDITION_NOT_MET when alive. */
|
||||
ret = ddsrt_proc_waitpid(pid, 0, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_PRECONDITION_NOT_MET);
|
||||
|
||||
/* Valid timeout should return DDS_RETCODE_TIMEOUT when alive. */
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(1), NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_TIMEOUT);
|
||||
|
||||
/* Kill it. */
|
||||
ret = ddsrt_proc_kill(pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* When killed, DDS_RETCODE_OK should be returned. */
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a sleeping process. Waiting for it should timeout.
|
||||
*/
|
||||
CU_Test(ddsrt_process, waitpids_timeout)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
/* Sleep for 20 seconds. We should have a timeout before then. */
|
||||
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
/* Timeout 0 should return DDS_RETCODE_PRECONDITION_NOT_MET when alive. */
|
||||
ret = ddsrt_proc_waitpids(0, NULL, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_PRECONDITION_NOT_MET);
|
||||
|
||||
/* Valid timeout should return DDS_RETCODE_TIMEOUT when alive. */
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(1), NULL, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_TIMEOUT);
|
||||
|
||||
/* Kill it. */
|
||||
ret = ddsrt_proc_kill(pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* When killed, DDS_RETCODE_OK should be returned. */
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(10), NULL, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
}
|
108
src/ddsrt/tests/process_app.c
Normal file
108
src/ddsrt/tests/process_app.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright(c) 2019 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "process_test.h"
|
||||
#include "dds/ddsrt/strtol.h"
|
||||
#include "dds/ddsrt/environ.h"
|
||||
#include "dds/ddsrt/process.h"
|
||||
|
||||
|
||||
static int test_create(void)
|
||||
{
|
||||
printf(" Process: created without args.\n");
|
||||
return TEST_CREATE_EXIT;
|
||||
}
|
||||
|
||||
static int test_sleep(int argi, int argc, char **argv)
|
||||
{
|
||||
argi++;
|
||||
if (argi < argc) {
|
||||
long long dorment;
|
||||
ddsrt_strtoll(argv[argi], NULL, 0, &dorment);
|
||||
printf(" Process: sleep %d seconds.\n", (int)dorment);
|
||||
dds_sleepfor(DDS_SECS((int64_t)dorment));
|
||||
} else {
|
||||
printf(" Process: no --sleep value.\n");
|
||||
return TEST_EXIT_WRONG_ARGS;
|
||||
}
|
||||
/* Expected to be destroyed before reaching this. */
|
||||
return TEST_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int test_pid(void)
|
||||
{
|
||||
int ret;
|
||||
ddsrt_pid_t pid;
|
||||
pid = ddsrt_getpid();
|
||||
ret = TEST_PID_EXIT(pid);
|
||||
printf(" Process: pid %d reduced to %d exit code.\n", (int)pid, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_env(void)
|
||||
{
|
||||
int ret = TEST_EXIT_FAILURE;
|
||||
char *envptr = NULL;
|
||||
if (ddsrt_getenv(TEST_ENV_VAR_NAME, &envptr) == DDS_RETCODE_OK) {
|
||||
printf(" Process: env %s=%s.\n", TEST_ENV_VAR_NAME, envptr);
|
||||
if (strcmp(envptr, TEST_ENV_VAR_VALUE) == 0) {
|
||||
ret = TEST_ENV_EXIT;
|
||||
}
|
||||
} else {
|
||||
printf(" Process: failed to get environment variable.\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_bslash(void)
|
||||
{
|
||||
printf(" Process: backslash argument.\n");
|
||||
return TEST_BSLASH_EXIT;
|
||||
}
|
||||
|
||||
static int test_dquote(void)
|
||||
{
|
||||
printf(" Process: double-quote argument.\n");
|
||||
return TEST_DQUOTE_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The spawned application used in the process tests.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (argc == 1) {
|
||||
ret = test_create();
|
||||
} else {
|
||||
if (strcmp(argv[1], TEST_SLEEP_ARG) == 0) {
|
||||
ret = test_sleep(1, argc, argv);
|
||||
} else if (strcmp(argv[1], TEST_PID_ARG) == 0) {
|
||||
ret = test_pid();
|
||||
} else if (strcmp(argv[1], TEST_ENV_ARG) == 0) {
|
||||
ret = test_env();
|
||||
} else if (strcmp(argv[1], TEST_BSLASH_ARG) == 0) {
|
||||
ret = test_bslash();
|
||||
} else if (strcmp(argv[1], TEST_DQUOTE_ARG) == 0) {
|
||||
ret = test_dquote();
|
||||
} else {
|
||||
printf(" Process: unknown argument.\n");
|
||||
ret = TEST_EXIT_WRONG_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
42
src/ddsrt/tests/process_test.h.in
Normal file
42
src/ddsrt/tests/process_test.h.in
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#ifndef DDSRT_TEST_PROCESS_TEST_H
|
||||
#define DDSRT_TEST_PROCESS_TEST_H
|
||||
|
||||
/* Get the application name from cmake to automatically
|
||||
* get the proper extension and location. */
|
||||
#define TEST_APPLICATION "@process_app_name@"
|
||||
|
||||
#define TEST_SLEEP_ARG "--sleep"
|
||||
|
||||
#define TEST_EXIT_GENERIC_OK (0)
|
||||
#define TEST_EXIT_FAILURE (1)
|
||||
#define TEST_EXIT_WRONG_ARGS (2)
|
||||
|
||||
#define TEST_CREATE_ARG NULL
|
||||
#define TEST_CREATE_EXIT (0)
|
||||
|
||||
#define TEST_PID_ARG "--retpid"
|
||||
#define TEST_PID_EXIT(pid) ((int)(int32_t)(pid % 127))
|
||||
|
||||
#define TEST_ENV_ARG "--checkenv"
|
||||
#define TEST_ENV_EXIT (12)
|
||||
#define TEST_ENV_VAR_NAME "TEST_ENV_VAR_NAME"
|
||||
#define TEST_ENV_VAR_VALUE "TEST_ENV_VAR_VALUE"
|
||||
|
||||
#define TEST_BSLASH_ARG "\\left\\\\right\\"
|
||||
#define TEST_BSLASH_EXIT (int)('\\')
|
||||
|
||||
#define TEST_DQUOTE_ARG "\"left\"\"right\""
|
||||
#define TEST_DQUOTE_EXIT (int)('"')
|
||||
|
||||
#endif /* DDSRT_TEST_PROCESS_TEST_H */
|
Loading…
Add table
Add a link
Reference in a new issue