/* Copyright (C) 2005 Bjoern Beutel. */
/* Description. =============================================================*/
/* This module manages child processes with optional input and/or output
* pipes. */
/* Includes. ================================================================*/
#define _POSIX_SOURCE
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <setjmp.h>
#include <glib.h>
#include "basic.h"
#include "files.h"
#include "input.h"
#include "processes.h"
#ifdef POSIX
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/wait.h>
#endif
#ifdef WIN32
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#endif
/* Functions. ===============================================================*/
bool_t
start_process( process_t *process )
/* Check whether PROCESS is still running. If not, start PROCESS anew by
* executing PROCESS->COMMAND_LINE.
* The fields PROCESS->USE_INPUT, PROCESS->USE_OUTPUT, PROCESS->NAME and
* PROCESS->COMMAND_LINE must have been set.
* If PROCESS->USE_INPUT == TRUE, open a pipe from the child process to the
* parent process. Connect it to stdout in the child process and to
* INPUT_STREAM in the parent process.
* If PROCESS->USE_OUTPUT == TRUE, open a pipe from the parent process to the
* child process. Connect it to OUTPUT_STREAM in the parent process and to
* stdin in the child process.
* Return TRUE if process has actually been started now. */
{
#ifdef POSIX
int to_child_fd[2];
int from_child_fd[2];
string_t arguments, argument;
string_t *args;
int_t arg_count, i;
if (process->id != 0)
{
if (waitpid( (pid_t) process->id, NULL, WNOHANG ) == 0)
return FALSE;
process->id = 0;
close_stream( &process->input_stream, NULL );
close_stream( &process->output_stream, NULL );
}
if (process->command_line == NULL)
complain( "Missing %s command line.", process->name );
if (process->use_output && pipe( to_child_fd ) == -1)
{
complain( "Can't create pipe to %s: %s.",
process->name, strerror( errno ) );
}
if (process->use_input && pipe( from_child_fd ) == -1)
{
complain( "Can't create pipe from %s: %s.",
process->name, strerror( errno ) );
}
signal( SIGPIPE, SIG_IGN );
switch (process->id = (ptr_t) fork())
{
case -1:
complain( "Can't create %s: %s.", process->name, strerror( errno ) );
break;
case 0:
if (process->use_output)
{
dup2( to_child_fd[0], STDIN_FILENO );
close( to_child_fd[0] );
close( to_child_fd[1] );
}
if (process->use_input)
{
dup2( from_child_fd[1], STDOUT_FILENO );
close( from_child_fd[0] );
close( from_child_fd[1] );
}
signal( SIGINT, SIG_IGN );
/* Count arguments. */
arg_count = 0;
arguments = process->command_line;
while (*arguments != EOS)
{
argument = parse_word( &arguments );
free_mem( &argument );
arg_count++;
}
/* Create argument vector. */
args = new_vector( sizeof( string_t ), arg_count + 1 );
arguments = process->command_line;
for (i = 0; i < arg_count; i++)
args[i] = parse_word( &arguments );
args[i] = NULL;
/* Execute command line. */
execvp( args[0], (char **) args );
fprintf( stderr, "Can't start %s \"%s\": %s.\n",
process->name, args[0], strerror( errno ) );
exit(1);
default:
if (process->use_output)
{
close( to_child_fd[0] );
process->output_stream = fdopen( to_child_fd[1], "w" );
if (process->output_stream == NULL)
{
complain( "Can't open pipe to %s: %s.",
process->name, strerror( errno ) );
}
}
if (process->use_input)
{
close( from_child_fd[1] );
process->input_stream = fdopen( from_child_fd[0], "r" );
if (process->input_stream == NULL)
{
complain( "Can't open pipe from %s: %s.",
process->name, strerror( errno ) );
}
}
}
return TRUE;
#endif
#ifdef WIN32
HANDLE parent_read, parent_write, parent_read_local, parent_write_local;
HANDLE child_read, child_write, old_stdin, old_stdout;
SECURITY_ATTRIBUTES security;
STARTUPINFO startup_info;
PROCESS_INFORMATION process_info;
DWORD status;
int fd;
/* Check if the process is still running. */
if (process->id != 0)
{
if (GetExitCodeProcess( (HANDLE) process->id, &status )
&& status == STILL_ACTIVE)
{
return FALSE;
}
CloseHandle( (HANDLE) process->id );
process->id = 0;
close_stream( &process->input_stream, NULL );
close_stream( &process->output_stream, NULL );
}
/* Check command line. */
if (process->command_line == NULL)
complain( "Missing transmit command line." );
/* We need pipes with inherited handles. */
security.nLength = sizeof( SECURITY_ATTRIBUTES );
security.bInheritHandle = TRUE;
security.lpSecurityDescriptor = NULL;
/* Create a pipe from the parent to the child. */
if (process->use_output)
{
if (! CreatePipe( &child_read, &parent_write, &security, 0 ))
complain( "Can't create pipe to %s.", process->name );
/* Change STDIN for the child process. */
old_stdin = GetStdHandle( STD_INPUT_HANDLE );
SetStdHandle( STD_INPUT_HANDLE, child_read );
/* Make sure the parent's handle is not inherited. */
DuplicateHandle( GetCurrentProcess(), parent_write,
GetCurrentProcess(), &parent_write_local,
0, FALSE, DUPLICATE_SAME_ACCESS );
CloseHandle( parent_write );
}
/* Create a pipe from the child to the parent. */
if (process->use_input)
{
if (! CreatePipe( &parent_read, &child_write, &security, 0 ))
complain( "Can't create pipe from %s.", process->name );
/* Change STDOUT for the child process. */
old_stdout = GetStdHandle( STD_OUTPUT_HANDLE );
SetStdHandle( STD_OUTPUT_HANDLE, child_write );
/* Make sure the parent's handle is not inherited. */
DuplicateHandle( GetCurrentProcess(), parent_read,
GetCurrentProcess(), &parent_read_local,
0, FALSE, DUPLICATE_SAME_ACCESS );
CloseHandle( parent_read );
}
/* Start the process as child. */
ZeroMemory( &startup_info, sizeof( startup_info ) );
startup_info.cb = sizeof( startup_info );
if (! CreateProcess( NULL, process->command_line, NULL, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP,
NULL, NULL, &startup_info, &process_info ))
{
complain( "Can't create %s.", process->name );
}
process->id = (ptr_t) process_info.hProcess;
CloseHandle( process_info.hThread );
if (process->use_output)
{
/* Reset stdin for parent. */
SetStdHandle( STD_INPUT_HANDLE, old_stdin );
CloseHandle( child_read );
/* Open stream to write to. */
fd = _open_osfhandle( (long) parent_write_local, O_TEXT );
process->output_stream = _fdopen( fd, "w" );
if (process->output_stream == NULL)
{
complain( "Can't open pipe to %s: %s.",
process->name, strerror( errno ) );
}
}
if (process->use_input)
{
/* Reset stdout for parent. */
SetStdHandle( STD_OUTPUT_HANDLE, old_stdout );
CloseHandle( child_write );
/* Open stream to read from. */
fd = _open_osfhandle( (long) parent_read_local, O_TEXT );
process->input_stream = _fdopen( fd, "r" );
if (process->input_stream == NULL)
{
complain( "Can't open pipe from %s: %s.",
process->name, strerror( errno ) );
}
}
return TRUE;
#endif
}
/*---------------------------------------------------------------------------*/
void
stop_process( process_t *process )
/* Stop the Malaga PROCESS. */
{
close_stream( &process->input_stream, NULL );
close_stream( &process->output_stream, NULL );
free_mem( &process->command_line );
if (process->id != 0)
{
#ifdef POSIX
kill( (pid_t) process->id, SIGTERM );
waitpid( (pid_t) process->id, NULL, 0 );
#endif
#ifdef WIN32
TerminateProcess( (HANDLE) process->id, 0 );
CloseHandle( (HANDLE) process->id );
#endif
process->id = 0;
}
}
/* End of file. =============================================================*/