Rudimentary process management.
Signed-off-by: Martin Bremmer <martin.bremmer@adlinktech.com>
This commit is contained in:
parent
386d5d3029
commit
5a8197fa2b
9 changed files with 1378 additions and 33 deletions
|
@ -74,7 +74,6 @@ list(APPEND sources
|
||||||
"${source_path}/io.c"
|
"${source_path}/io.c"
|
||||||
"${source_path}/log.c"
|
"${source_path}/log.c"
|
||||||
"${source_path}/retcode.c"
|
"${source_path}/retcode.c"
|
||||||
"${source_path}/process.c"
|
|
||||||
"${source_path}/strtod.c"
|
"${source_path}/strtod.c"
|
||||||
"${source_path}/strtol.c")
|
"${source_path}/strtol.c")
|
||||||
|
|
||||||
|
@ -104,7 +103,7 @@ list(APPEND sources
|
||||||
# network stack. In order to mix-and-match various compilers, architectures,
|
# network stack. In order to mix-and-match various compilers, architectures,
|
||||||
# operating systems, etc input from the build system is required.
|
# operating systems, etc input from the build system is required.
|
||||||
foreach(feature atomics cdtors environ heap ifaddrs random rusage
|
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")
|
if(EXISTS "${include_path}/dds/ddsrt/${feature}.h")
|
||||||
list(APPEND headers "${include_path}/dds/ddsrt/${feature}.h")
|
list(APPEND headers "${include_path}/dds/ddsrt/${feature}.h")
|
||||||
file(GLOB
|
file(GLOB
|
||||||
|
|
|
@ -13,11 +13,9 @@
|
||||||
#define DDSRT_PROCESS_H
|
#define DDSRT_PROCESS_H
|
||||||
|
|
||||||
#include "dds/export.h"
|
#include "dds/export.h"
|
||||||
|
#include "dds/ddsrt/time.h"
|
||||||
#include "dds/ddsrt/types.h"
|
#include "dds/ddsrt/types.h"
|
||||||
|
#include "dds/ddsrt/retcode.h"
|
||||||
#if defined (__cplusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
typedef DWORD ddsrt_pid_t;
|
typedef DWORD ddsrt_pid_t;
|
||||||
|
@ -33,6 +31,11 @@ typedef pid_t ddsrt_pid_t;
|
||||||
#endif
|
#endif
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return process ID (PID) of the calling process.
|
* @brief Return process ID (PID) of the calling process.
|
||||||
*
|
*
|
||||||
|
@ -41,6 +44,168 @@ typedef pid_t ddsrt_pid_t;
|
||||||
DDS_EXPORT ddsrt_pid_t
|
DDS_EXPORT ddsrt_pid_t
|
||||||
ddsrt_getpid(void);
|
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)
|
#if defined (__cplusplus)
|
||||||
}
|
}
|
||||||
#endif
|
#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"
|
"random.c"
|
||||||
"strlcpy.c"
|
"strlcpy.c"
|
||||||
"socket.c"
|
"socket.c"
|
||||||
|
"process.c"
|
||||||
"select.c")
|
"select.c")
|
||||||
|
|
||||||
add_cunit_executable(cunit_ddsrt ${sources})
|
add_cunit_executable(cunit_ddsrt ${sources})
|
||||||
|
@ -42,3 +43,26 @@ endif()
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
cunit_ddsrt PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
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