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