-/*
- * src/command.c - type representing a command.
- *
- * Copyright 2008,2009 Martin Quinson, Malek Cherier All right reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package.
- *
- * Purpose:
- * This file contains all the definitions of the functions related with
- * the tesh command type.
- *
- */
-#include <unit.h>
-#include <command.h>
-#include <context.h>
-#include <writer.h>
-#include <reader.h>
-#include <timer.h>
-
-#ifndef WIN32
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#endif
-
-#include <com.h>
-
-#include <xsignal.h>
-
-#include <is_cmd.h>
-
-XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
-
-static void*
-command_start(void* p);
-
-
-command_t
-command_new(unit_t unit, context_t context, xbt_os_mutex_t mutex)
-{
- command_t command;
-
- command = xbt_new0(s_command_t, 1);
-
- /* get the context of the execution of the command */
- if(!(command->context = context_dup(context)))
- {
- free(command);
- return NULL;
- }
-
- /* the exit code of the command is indefinite */
- command->exit_code = INDEFINITE;
-
- /* the signal of the command is indefinite */
- command->signal = INDEFINITE_SIGNAL;
-
- command->failed = 0;
- command->interrupted = 0;
-
- /* the mutex used to safetly access to the command unit properties */
- command->mutex = mutex;
-
- command->output = xbt_strbuff_new();
-
- command->pid = INDEFINITE_PID;
-
- command->stat_val = -1;
-
- /* set the unit of the command */
- command->root = unit->root ? unit->root : unit;
- command->unit = unit;
-
- /* all the commands are runned in a thread */
- command->thread = NULL;
-
- command->successeded = 0;
-
- command->reader = reader_new(command);
-
- if(context->input->used)
- command->writer = writer_new(command);
- else
- command->writer = NULL;
-
- if(context->timeout != INDEFINITE)
- command->timer = timer_new(command);
- else
- command->timer = NULL;
-
- command->status = cs_initialized;
- command->reason = csr_unknown;
-
- command->stdin_fd = INDEFINITE_FD;
- command->stdout_fd = INDEFINITE_FD;
-
-
- /* register the command */
- xbt_os_mutex_acquire(mutex);
-
- xbt_dynar_push(unit->commands, &command);
- command->root->cmd_nb++;
- xbt_os_mutex_release(mutex);
-
- #ifndef WIN32
- command->killed = 0;
- command->execlp_errno = 0;
- #endif
-
-
- return command;
-}
-
-int
-command_run(command_t command)
-{
-
- if(!silent_flag)
- INFO2("[%s] %s",command->context->pos, command->context->command_line);
-
- if(!just_print_flag)
- {
- if(!interrupted)
- {
- /* start the command in a thread*/
- if(command->context->async)
- {
- command->thread = xbt_os_thread_create("", command_start, command);
-
- }
- else
- {
- /* start the command in the main thread */
- command_start(command);
- }
- }
- else
- {
- command_interrupt(command);
- }
- }
-
- return 0;
-
-}
-
-static void*
-command_start(void* p)
-{
- command_t command = (command_t)p;
- unit_t root = command->root;
-
- /* the command is started */
- command->status = cs_started;
-
- /* increment the number of started commands of the unit */
- xbt_os_mutex_acquire(command->mutex);
- (root->started_cmd_nb)++;
- xbt_os_mutex_release(command->mutex);
-
- /* execute the command of the test */
- command_exec(command, command->context->command_line);
-
- if(cs_in_progress == command->status)
- {
- /* wait the process if it is in progress */
- command_wait(command);
-
- if(cs_failed != command->status && cs_interrupted != command->status)
- command_check(command);
- }
-
- xbt_os_mutex_acquire(command->mutex);
-
- /* if it's the last command of the root unit */
- if(!root->interrupted && root->parsed && (root->started_cmd_nb == (root->failed_cmd_nb + root->interrupted_cmd_nb + root->successeded_cmd_nb)))
- {
- /* first release the mutex */
- root->released = 1;
- xbt_os_mutex_release(command->mutex);
- /* the last command release the unit */
- xbt_os_sem_release(root->sem);
- }
- else
- xbt_os_mutex_release(command->mutex);
-
-
- /* wait the end of the timer, the reader and the writer */
- if(command->timer && command->timer->thread)
- timer_wait(command->timer);
-
- /* wait the end of the writer */
- if(command->writer && command->writer->thread)
- writer_wait(command->writer);
-
- /* wait the end of the reader */
- if(command->reader && command->reader->thread)
- reader_wait(command->reader);
-
-
-
- return NULL;
-}
-
-#ifdef WIN32
-void
-command_exec(command_t command, const char* command_line)
-{
-
- STARTUPINFO si = {0}; /* contains the informations about the child process windows*/
- PROCESS_INFORMATION pi = {0}; /* contains child process informations */
- SECURITY_ATTRIBUTES sa = {0}; /* contains the security descriptor for the pipe handles */
- HANDLE child_stdin_handle[2] = {NULL}; /* child_stdin_handle[1] <-> stdout of the child process */
- HANDLE child_stdout_handle[2] = {NULL}; /* child_stdout_handle[0] <-> stdin of the child process */
- HANDLE child_stderr = NULL;
-
- sa.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa.lpSecurityDescriptor = NULL; /* use default security for the pipe handles */
-
- sa.bInheritHandle = TRUE; /* the pipe handles can be inherited */
-
- if(!CreatePipe(&(child_stdin_handle[0]),&(child_stdin_handle[1]),&sa,0))
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));
-
- unit_set_error(command->unit, (int)GetLastError(), 0);
-
- command->failed = 1;
- command->status = cs_failed;
-
- return;
- }
-
-
- if(!DuplicateHandle(GetCurrentProcess(),(child_stdin_handle[1]),GetCurrentProcess(),&(child_stderr),0,TRUE,DUPLICATE_SAME_ACCESS))
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));
-
- unit_set_error(command->unit, (int)GetLastError(), 0);
-
- CloseHandle(child_stdin_handle[0]);
- CloseHandle(child_stdin_handle[1]);
-
- command->failed = 1;
- command->status = cs_failed;
-
- return;
- }
-
- if(!CreatePipe(&(child_stdout_handle[0]),&(child_stdout_handle[1]),&sa,0))
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));
- unit_set_error(command->unit, (int)GetLastError(), 0);
-
- CloseHandle(child_stdout_handle[0]);
- CloseHandle(child_stdout_handle[1]);
- CloseHandle(child_stdin_handle[0]);
- CloseHandle(child_stdin_handle[1]);
-
- command->failed = 1;
- command->status = cs_failed;
-
- return;
- }
-
- /* Read handle for read operations on the child std output. */
- if(!DuplicateHandle(GetCurrentProcess(),(child_stdin_handle[0]),GetCurrentProcess(),&(command->stdout_fd),0,FALSE, DUPLICATE_SAME_ACCESS))
- {
- CloseHandle(child_stdout_handle[0]);
- CloseHandle(child_stdout_handle[1]);
- CloseHandle(child_stdin_handle[0]);
- CloseHandle(child_stdin_handle[1]);
-
- command->failed = 1;
- command->status = cs_failed;
-
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));
- unit_set_error(command->unit, (int)GetLastError(), 0);
-
- return;
- }
-
-
- /* Write handle for write operations on the child std input. */
- if(!DuplicateHandle(GetCurrentProcess(),(child_stdout_handle[1]),GetCurrentProcess(),&(command->stdin_fd), 0,FALSE,DUPLICATE_SAME_ACCESS))
- {
- CloseHandle(child_stdout_handle[0]);
- CloseHandle(child_stdout_handle[1]);
- CloseHandle(child_stdin_handle[0]);
- CloseHandle(child_stdin_handle[1]);
-
- command->failed = 1;
- command->status = cs_failed;
-
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));
-
- unit_set_error(command->unit, (int)GetLastError(), 0);
-
- return;
- }
-
-
- CloseHandle(child_stdin_handle[0]);
- CloseHandle(child_stdout_handle[1]);
-
- if(command->timer)
- {
- /* launch the timer */
- timer_time(command->timer);
- }
-
- if(command->reader)
- {
- /* launch the reader */
- reader_read(command->reader);
- }
-
-
- if(command->writer)
- {
- /* launch the writer */
- writer_write(command->writer);
- }
-
- si.cb = sizeof(STARTUPINFO);
-
- si.dwFlags |= STARTF_USESTDHANDLES;
- si.hStdOutput = child_stdin_handle[1];
- si.hStdInput = child_stdout_handle[0];
- si.hStdError = child_stderr;
-
- /* launch the process */
- if(!CreateProcess(
- NULL,
- (char*)command_line,
- NULL,
- NULL,
- TRUE,
- CREATE_NO_WINDOW,
- NULL,
- NULL,
- &si,
- &pi)
- )
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));
-
- command_handle_failure(command,csr_create_process_function_failure);
-
- unit_set_error(command->unit, (int)GetLastError(), 0);
- }
- else
- {
- /* the command is running */
- command->status = cs_in_progress;
-
- /* save the pid of the command */
- command->pid = pi.hProcess;
-
- /* close non used thread handle */
- CloseHandle(pi.hThread);
-
- }
-
-
- /* close non used handles */
- CloseHandle(child_stdin_handle[1]);
- CloseHandle(child_stdout_handle[0]);
- CloseHandle(child_stderr);
-
-
-}
-#else
-void
-command_exec(command_t command, const char* command_line)
-{
- int child_stdin_fd[2] ;
- int child_stdout_fd[2];
-
- #ifdef __CHKCMD
- int rv = is_cmd(command->unit->runner->path, command->unit->runner->builtin, command_line);
-
- if(rv != 0)
- {
-
- if(rv == EINVAL)
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(rv, 0));
- unit_set_error(command->unit, rv, 0);
- }
- else
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(rv, 1));
- unit_set_error(command->unit, rv, 1);
- }
-
- command_handle_failure(command, csr_command_not_found);
-
- return;
- }
-
- #endif
-
-
- if(command->writer)
- {
- if(pipe(child_stdin_fd))
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));
-
- unit_set_error(command->unit, errno, 0);
-
- command_handle_failure(command, csr_pipe_function_failed);
-
-
-
- return;
- }
- }
-
- if(command->reader)
- {
- if(pipe(child_stdout_fd))
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));
-
- if(command->writer)
- {
- close(child_stdin_fd[0]);
- close(child_stdin_fd[1]);
- }
-
- unit_set_error(command->unit, errno, 0);
-
- command_handle_failure(command, csr_pipe_function_failed);
-
- return;
- }
- }
-
- if(command->writer)
- {
- if(fcntl(child_stdin_fd[1], F_SETFL, fcntl(child_stdin_fd[1], F_GETFL) | O_NONBLOCK) < 0)
- {
-
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));
-
- close(child_stdin_fd[0]);
- close(child_stdin_fd[1]);
-
- if(command->reader)
- {
- close(child_stdout_fd[0]);
- close(child_stdout_fd[1]);
- }
-
- unit_set_error(command->unit, errno, 0);
-
- command_handle_failure(command, csr_fcntl_function_failed);
-
- return;
- }
- }
-
- /* to write to the child stdin */
- command->stdin_fd = child_stdin_fd[1];
-
- /* to read from the child stdout */
- command->stdout_fd = child_stdout_fd[0];
-
- /* launch the reader if any*/
- if(command->reader)
- reader_read(command->reader);
-
- /* launch the writer if any */
- if(command->writer)
- writer_write(command->writer);
-
- /* launch the timer if any */
- if(command->timer)
- timer_time(command->timer);
-
- /* if there is a reader wait for its starting */
- if(command->reader)
- xbt_os_sem_acquire(command->reader->started);
-
- /* if there is a reader wait for its ending */
- if(command->writer)
- xbt_os_sem_acquire(command->writer->written);
-
- /* if there is a reader wait for its starting */
- if(command->timer)
- xbt_os_sem_acquire(command->timer->started);
-
- /* update the state of the command, assume it is in progress */
- command->status = cs_in_progress;
-
- command->pid= fork();
-
- if(command->pid < 0)
- {/* error */
- if(command->writer)
- {
- close(child_stdin_fd[0]);
- close(child_stdin_fd[1]);
- }
-
- if(command->reader)
- {
- close(child_stdout_fd[0]);
- close(child_stdout_fd[1]);
- }
-
- ERROR2("[%s] Cannot fork the command `%s'", command->context->pos, command->context->command_line);
- unit_set_error(command->unit, errno, 0);
- command_handle_failure(command,csr_fork_function_failure);
- }
- else
- {
- if(command->pid)
- {/* father */
-
- /* close unused file descriptors */
- if(command->writer)
- close(child_stdin_fd[0]);
-
- if(command->reader)
- close(child_stdout_fd[1]);
- }
- else
- {/* child */
-
- /* close unused file descriptors */
- if(command->writer)
- close(child_stdin_fd[1]);
-
- if(command->reader)
- close(child_stdout_fd[0]);
-
- if(command->writer)
- {
- /* redirect stdin to child_stdin_fd[0] (now fgets(), getchar() ... read from the pipe */
- if(dup2(child_stdin_fd[0],STDIN_FILENO) < 0)
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));
- command->unit->exit_code = errno;
-
- unit_set_error(command->unit, errno, 0);
- command_handle_failure(command,csr_dup2_function_failure);
- }
-
- /* close the unused file descriptor */
- close(child_stdin_fd[0]);
- }
-
- if(command->reader)
- {
-
- /* redirect stdout and stderr to child_stdout_fd[1] (now printf(), perror()... write to the pipe */
- if(dup2(child_stdout_fd[1],STDOUT_FILENO) < 0)
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));
-
- unit_set_error(command->unit, errno, 0);
- command_handle_failure(command, csr_dup2_function_failure);
- }
-
- if(dup2(child_stdout_fd[1], STDERR_FILENO) < 0)
- {
- ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));
- unit_set_error(command->unit, errno, 0);
- command_handle_failure(command, csr_dup2_function_failure);
- }
-
- /* close the unused file descriptor */
- close(child_stdout_fd[1]);
- }
-
- /* launch the command */
- if(execlp("/bin/sh", "sh", "-c", command->context->command_line, NULL) < 0)
- command->execlp_errno = errno;
- }
- }
-}
-#endif
-
-#ifdef WIN32
-void
-command_wait(command_t command)
-{
- /* wait for the command terminaison */
- DWORD rv;
-
- if(WAIT_FAILED == WaitForSingleObject(command->pid, INFINITE))
- {
- ERROR2("[%s] Cannot wait for the child`%s'", command->context->pos, command->context->command_line);
-
- unit_set_error(command->unit, (int)GetLastError(), 0);
-
- command_handle_failure(command, csr_wait_failure );
- /* TODO : see for the interruption */
- }
- else
- {
- /* don't take care of the timer or the writer or the reader failue */
- if(cs_failed != command->status && cs_interrupted != command->status)
- {
- if(!GetExitCodeProcess(command->pid,&rv))
- {
- ERROR2("[%s] Cannot get the exit code of the process `%s'",command->context->pos, command->context->command_line);
-
- unit_set_error(command->unit, (int)GetLastError(), 0);
-
- command_handle_failure(command, csr_get_exit_code_process_function_failure );
- }
- else
- command->stat_val = command->exit_code = rv;
- }
- }
-}
-#else
-void
-command_wait(command_t command)
-{
- if(!command->execlp_errno)
- {
- /* let this thread wait for the child so that the main thread can detect the timeout without blocking on the wait */
- int pid = waitpid(command->pid, &(command->stat_val), 0);
-
- if(pid != command->pid)
- {
- ERROR2("[%s] Cannot wait for the child`%s'", command->context->pos, command->context->command_line);
-
- unit_set_error(command->unit, errno, 0);
-
- command_handle_failure(command, csr_waitpid_function_failure);
- }
- else
- {
- if(WIFEXITED(command->stat_val))
- command->exit_code = WEXITSTATUS(command->stat_val);
- }
- }
- else
- {
- ERROR2("[%s] Cannot execute the command `%s'", command->context->pos, command->context->command_line);
-
- unit_set_error(command->unit, command->execlp_errno, 0);
-
- command_handle_failure(command, csr_execlp_function_failure);
- }
-}
-#endif
-
-void
-command_check(command_t command)
-{
- int success = 1;
- cs_reason_t reason;
-
- /* we have a signal, store it */
- if(WIFSIGNALED(command->stat_val))
- {
- command->signal = strdup(signal_name(WTERMSIG(command->stat_val),command->context->signal));
- }
-
- /* we have a signal and no signal is expected */
- if(WIFSIGNALED(command->stat_val) && !command->context->signal)
- {
- success = 0;
- ERROR3("[%s] `%s' : NOK (unexpected signal `%s' caught)", command->context->pos, command->context->command_line, command->signal);
-
- unit_set_error(command->unit, EUNXPSIG, 1);
-
- reason = csr_unexpected_signal_caught;
- }
-
- /* we have a signal that differ form the expected signal */
- if(WIFSIGNALED(command->stat_val) && command->context->signal && strcmp(signal_name(WTERMSIG(command->stat_val),command->context->signal),command->context->signal))
- {
-
- ERROR4("[%s] `%s' : NOK (got signal `%s' instead of `%s')", command->context->pos, command->context->command_line, command->signal, command->context->signal);
-
- if(success)
- {
- success = 0;
- unit_set_error(command->unit, ESIGNOTMATCH, 1);
- }
-
- reason = csr_signals_dont_match;
- }
-
- /* we don't receipt the expected signal */
- if(!WIFSIGNALED(command->stat_val) && command->context->signal)
- {
-
- ERROR3("[%s] `%s' : NOK (expected `%s' not receipt)", command->context->pos, command->context->command_line, command->context->signal);
-
- if(success)
- {
- success = 0;
- unit_set_error(command->unit, ESIGNOTRECEIPT, 1);
- }
-
- reason = csr_expected_signal_not_receipt;
- }
-
- /* if the command exit normaly and we expect a exit code : test it */
- if(WIFEXITED(command->stat_val) /* && INDEFINITE != command->context->exit_code*/)
- {
- /* the exit codes don't match */
- if(WEXITSTATUS(command->stat_val) != command->context->exit_code)
- {
- ERROR4("[%s] %s : NOK (returned code `%d' instead `%d')", command->context->pos, command->context->command_line, WEXITSTATUS(command->stat_val), command->context->exit_code);
-
- if(success)
- {
- success = 0;
- unit_set_error(command->unit, EEXITCODENOTMATCH, 1);
- }
-
- reason = csr_exit_codes_dont_match;
- }
- }
-
- /* make sure the reader done */
- while(!command->reader->done)
- xbt_os_thread_yield();
-
- #ifdef WIN32
- CloseHandle(command->stdout_fd);
- #else
- close(command->stdout_fd);
- #endif
-
- command->stdout_fd = INDEFINITE_FD;
-
- xbt_strbuff_chomp(command->output);
- xbt_strbuff_chomp(command->context->output);
- xbt_strbuff_trim(command->output);
- xbt_strbuff_trim(command->context->output);
-
- if(!success)
- {
- xbt_dynar_t a = xbt_str_split(command->output->data, "\n");
- char *out = xbt_str_join(a,"\n||");
- xbt_dynar_free(&a);
- INFO2("Output of <%s> so far: \n||%s", command->context->pos,out);
- free(out);
- }
- /* if ouput handling flag is specified check the output */
- else if(oh_check == command->context->output_handling && command->reader)
- {
- if(command->output->used != command->context->output->used || strcmp(command->output->data, command->context->output->data))
- {
- char *diff;
-
- ERROR2("[%s] `%s' : NOK (outputs mismatch):", command->context->pos, command->context->command_line);
-
- if(success)
- {
- unit_set_error(command->unit, EOUTPUTNOTMATCH, 1);
- success = 0;
- }
-
- reason = csr_outputs_dont_match;
-
- /* display the diff */
- diff = xbt_str_diff(command->context->output->data,command->output->data);
- INFO1("%s",diff);
- free(diff);
- }
- }
- else if (oh_ignore == command->context->output_handling)
- {
- INFO1("(ignoring the output of <%s> as requested)",command->context->line);
- }
- else if (oh_display == command->context->output_handling)
- {
- xbt_dynar_t a = xbt_str_split(command->output->data, "\n");
- char *out = xbt_str_join(a,"\n||");
- xbt_dynar_free(&a);
- INFO3("[%s] Here is the (ignored) command `%s' output: \n||%s",command->context->pos, command->context->command_line, out);
- free(out);
- }
-
- if(success)
- {
- xbt_os_mutex_acquire(command->mutex);
-
- if(command->status != cs_interrupted)
- {
- /* signal the success of the command */
- command->status = cs_successeded;
- command->successeded = 1;
-
- /* increment the number of successeded command of the unit */
- (command->root->successeded_cmd_nb)++;
- }
-
- xbt_os_mutex_release(command->mutex);
- }
- else
- {
- command_handle_failure(command, reason);
- }
-}
-
-#ifdef WIN32
-void
-command_kill(command_t command)
-{
- if(INDEFINITE_PID != command->pid)
- {
- INFO2("[%s] Kill the process `%s'", command->context->pos, command->context->command_line);
- TerminateProcess(command->pid, INDEFINITE);
- }
-}
-#else
-void
-command_kill(command_t command)
-{
- if(INDEFINITE_PID != command->pid)
- {
- kill(command->pid,SIGTERM);
-
- if(!command->context->signal)
- command->context->signal = strdup("SIGTERM");
-
- command->exit_code = INDEFINITE;
- command->killed = 1;
-
- usleep(100);
-
- INFO2("[%s] Kill the process `%s'", command->context->pos, command->context->command_line);
- kill(command->pid,SIGKILL);
-
-
- }
-}
-#endif
-
-void
-command_interrupt(command_t command)
-{
- xbt_os_mutex_acquire(command->mutex);
-
- if((command->status != cs_interrupted) && (command->status != cs_failed) && (command->status != cs_successeded))
- {
- command->status = cs_interrupted;
- command->reason = csr_interruption_request;
- command->interrupted = 1;
- command->unit->interrupted = 1;
-
- xbt_os_mutex_acquire(command->root->mutex);
- (command->root->interrupted_cmd_nb)++;
- xbt_os_mutex_release(command->root->mutex);
-
- if(command->pid != INDEFINITE_PID)
- command_kill(command);
- }
-
- xbt_os_mutex_release(command->mutex);
-
-
-}
-
-void
-command_summarize(command_t command)
-{
- if(cs_successeded != command->status)
- {
-
- #ifndef WIN32
- if(command->killed)
- printf(" <killed command>\n");
- #endif
-
- /* display the reason of the status of the command */
- switch(command->reason)
- {
- /* the function pipe or CreatePipe() fails */
- case csr_pipe_function_failed :
- printf(" reason : pipe() or CreatePipe() function failed (system error)\n");
- break;
-
- case csr_shell_failed :
- printf(" reason : shell failed (may be command not found)\n");
- break;
-
- case csr_get_exit_code_process_function_failure :
- printf(" reason : ExitCodeProcess() function failed (system error)\n");
- break;
-
- /* reader failure reasons*/
- case csr_read_pipe_broken :
- printf(" reason : command read pipe broken\n");
- break;
-
- case csr_read_failure :
- printf(" reason : command stdout read failed\n");
- break;
-
- /* writer failure reasons */
- case csr_write_failure :
- printf(" reason : command stdin write failed\n");
- break;
-
- case csr_write_pipe_broken :
- printf(" reason : command write pipe broken\n");
- break;
-
- /* timer reason */
- case csr_timeout :
- printf(" reason : command timeouted\n");
- break;
-
- /* command failure reason */
- case csr_command_not_found :
- printf(" reason : command not found\n");
- break;
-
- /* context failure reasons */
- case csr_exit_codes_dont_match :
- printf(" reason : exit codes don't match\n");
-
- break;
-
- /* dup2 function failure reasons */
- case csr_dup2_function_failure :
- printf(" reason : dup2() function failed\n");
-
- break;
-
- /* execlp function failure reasons */
- case csr_execlp_function_failure :
- printf(" reason : execlp() function failed\n");
-
- break;
-
- /* waitpid function failure reasons */
- case csr_waitpid_function_failure :
- printf(" reason : waitpid() function failed\n");
-
- break;
-
- /* CreateProcess function failure reasons */
- case csr_create_process_function_failure :
- printf(" reason : CreateProcesss() function failed\n");
-
- break;
-
- case csr_outputs_dont_match :
- {
- char *diff;
- printf(" reason : ouputs don't match\n");
- diff = xbt_str_diff(command->context->output->data,command->output->data);
- printf(" output diff :\n%s\n",diff);
- free(diff);
- }
-
- break;
-
- case csr_signals_dont_match :
- printf(" reason : signals don't match\n");
- break;
-
- case csr_unexpected_signal_caught:
- printf(" reason : unexpected signal caught\n");
- break;
-
- case csr_expected_signal_not_receipt :
- printf(" reason : expected signal not receipt\n");
- break;
-
- /* system failure reasons */
- case csr_fork_function_failure :
- printf(" reason : fork function failed\n");
- break;
-
- case csr_wait_failure :
- printf(" reason : wait command failure\n");
- break;
-
- /* global/local interruption */
- case csr_interruption_request :
- printf(" reason : the command receive a interruption request\n");
- break;
-
- /* unknown ? */
- case csr_unknown :
- printf(" reason : unknown \n");
- }
- }
-
- if(csr_command_not_found != command->reason && csr_fork_function_failure != command->reason && csr_execlp_function_failure != command->reason)
- {
- if(INDEFINITE != command->exit_code)
- /* the command exit code */
- printf(" exit code : %d\n",command->exit_code);
-
- /* if an expected exit code was specified display it */
- if(INDEFINITE != command->context->exit_code)
- printf(" expected exit code : %d\n",command->context->exit_code);
- else
- printf(" no expected exit code specified\n");
-
- /* if an expected exit code was specified display it */
- if(NULL == command->context->signal)
- printf(" no expected signal specified\n");
- else
- {
- if(NULL != command->signal)
- printf(" signal : %s\n",command->signal);
-
- printf(" expected signal : %s\n",command->context->signal);
- }
-
- /* if the command has out put and the metacommand display output is specified display it */
- if(command->output && (0 != command->output->used) && (oh_display == command->context->output_handling))
- {
- xbt_dynar_t a = xbt_str_split(command->output->data, "\n");
- char *out = xbt_str_join(a,"\n||");
- xbt_dynar_free(&a);
- printf(" output :\n||%s",out);
- free(out);
- }
- }
-
- printf("\n");
-}
-
-void
-command_handle_failure(command_t command, cs_reason_t reason)
-{
-
- unit_t root = command->root;
-
- xbt_os_mutex_acquire(command->mutex);
-
- if((command->status != cs_interrupted) && (command->status != cs_failed))
- {
- command->status = cs_failed;
- command->reason = reason;
- command->failed = 1;
-
- command->unit->failed = 1;
-
- xbt_os_mutex_acquire(root->mutex);
-
- /* increment the number of failed command of the unit */
- root->failed_cmd_nb++;
-
- /* if the --ignore-failures option is not specified */
- if(!keep_going_unit_flag)
- {
- if(!root->interrupted)
- {
- /* the unit interrupted (exit for the loop) */
- root->interrupted = 1;
-
- /* release the unit */
- xbt_os_sem_release(root->sem);
- }
-
- /* if the --keep-going option is not specified */
- if(!keep_going_flag)
- {
- if(!interrupted)
- {
- /* request an global interruption by the runner */
- interrupted = 1;
-
- /* release the runner */
- xbt_os_sem_release(units_sem);
- }
- }
- }
-
- xbt_os_mutex_release(root->mutex);
- }
-
- xbt_os_mutex_release(command->mutex);
-}
-
-int
-command_free(command_t* ptr)
-{
- /* close the stdin and the stdout pipe handles */
-
- #ifdef WIN32
- if((*ptr)->stdin_fd != INDEFINITE_FD)
- CloseHandle((*ptr)->stdin_fd);
-
- if((*ptr)->stdout_fd != INDEFINITE_FD)
- CloseHandle((*ptr)->stdout_fd);
-
- #else
-
- if((*ptr)->stdin_fd != INDEFINITE_FD)
- close((*ptr)->stdin_fd);
-
- if((*ptr)->stdout_fd != INDEFINITE_FD)
- close((*ptr)->stdout_fd);
- #endif
-
- if((*ptr)->timer)
- {
- if(timer_free(&((*ptr)->timer)) < 0)
- return -1;
- }
-
- if((*ptr)->writer)
- {
- if(writer_free(&((*ptr)->writer)) < 0)
- return -1;
- }
-
- if((*ptr)->reader)
- {
- if(reader_free(&((*ptr)->reader)) < 0)
- return -1;
- }
-
- if((*ptr)->output)
- xbt_strbuff_free((*ptr)->output);
-
- if((*ptr)->context)
- {
- if(context_free(&((*ptr)->context)) < 0)
- return -1;
- }
-
- if((*ptr)->signal)
- free((*ptr)->signal);
-
- free(*ptr);
-
- *ptr = NULL;
-
- return 0;
-}
-
-
-
+/*\r
+ * src/command.c - type representing a command.\r
+ *\r
+ * Copyright 2008,2009 Martin Quinson, Malek Cherier All right reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify it\r
+ * under the terms of the license (GNU LGPL) which comes with this package.\r
+ *\r
+ * Purpose:\r
+ * This file contains all the definitions of the functions related with\r
+ * the tesh command type.\r
+ *\r
+ */\r
+#include <unit.h>\r
+#include <command.h>\r
+#include <context.h>\r
+#include <writer.h>\r
+#include <reader.h>\r
+#include <timer.h>\r
+\r
+#ifndef WIN32\r
+#include <sys/types.h>\r
+#include <sys/wait.h>\r
+#include <sys/stat.h>\r
+#include <unistd.h>\r
+#else\r
+char *\r
+tow32cmd(const char* cmd)\r
+{\r
+ static char w32cmd[PATH_MAX + 1] = {0};\r
+ char cmd_buf[PATH_MAX + 1] = {0};\r
+ size_t i,j, len;\r
+\r
+ if(!cmd)\r
+ {\r
+ errno = EINVAL;\r
+ return NULL;\r
+ }\r
+\r
+ /* TODO : if ~*/\r
+ if(cmd[0] != '.')\r
+ {\r
+ strcpy(w32cmd, cmd);\r
+ return w32cmd;\r
+ }\r
+\r
+ i = j = 0;\r
+ len = strlen(cmd);\r
+\r
+ while(i < len)\r
+ {\r
+ if(cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != '>')\r
+ cmd_buf[j++] = cmd[i];\r
+ else\r
+ break;\r
+\r
+ i++;\r
+ }\r
+\r
+ _fullpath(w32cmd, cmd_buf, sizeof(w32cmd));\r
+\r
+ if(!strstr(w32cmd, ".exe"))\r
+ strcat(w32cmd, ".exe ");\r
+\r
+ strcat(w32cmd, cmd + i);\r
+\r
+\r
+ /*printf("w32cmd : %s", w32cmd);*/\r
+\r
+ return w32cmd;\r
+}\r
+#endif\r
+\r
+#include <com.h>\r
+\r
+#include <xsignal.h>\r
+\r
+#include <is_cmd.h>\r
+\r
+XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);\r
+\r
+static void*\r
+command_start(void* p);\r
+\r
+\r
+command_t\r
+command_new(unit_t unit, context_t context, xbt_os_mutex_t mutex)\r
+{\r
+ command_t command;\r
+\r
+ command = xbt_new0(s_command_t, 1);\r
+\r
+ /* get the context of the execution of the command */\r
+ if(!(command->context = context_dup(context)))\r
+ {\r
+ free(command);\r
+ return NULL;\r
+ }\r
+\r
+ /* the exit code of the command is indefinite */\r
+ command->exit_code = INDEFINITE;\r
+\r
+ /* the signal of the command is indefinite */\r
+ command->signal = INDEFINITE_SIGNAL;\r
+\r
+ command->failed = 0;\r
+ command->interrupted = 0;\r
+\r
+ /* the mutex used to safetly access to the command unit properties */\r
+ command->mutex = mutex;\r
+\r
+ command->output = xbt_strbuff_new();\r
+\r
+ command->pid = INDEFINITE_PID;\r
+\r
+ command->stat_val = -1;\r
+\r
+ /* set the unit of the command */\r
+ command->root = unit->root ? unit->root : unit;\r
+ command->unit = unit;\r
+\r
+ /* all the commands are runned in a thread */\r
+ command->thread = NULL;\r
+\r
+ command->successeded = 0;\r
+\r
+ command->reader = reader_new(command);\r
+\r
+ if(context->input->used)\r
+ command->writer = writer_new(command);\r
+ else\r
+ command->writer = NULL;\r
+\r
+ if(context->timeout != INDEFINITE)\r
+ command->timer = timer_new(command);\r
+ else\r
+ command->timer = NULL;\r
+\r
+ command->status = cs_initialized;\r
+ command->reason = csr_unknown;\r
+\r
+ command->stdin_fd = INDEFINITE_FD;\r
+ command->stdout_fd = INDEFINITE_FD;\r
+\r
+\r
+ /* register the command */\r
+ xbt_os_mutex_acquire(mutex);\r
+\r
+ xbt_dynar_push(unit->commands, &command);\r
+ command->root->cmd_nb++;\r
+ xbt_os_mutex_release(mutex);\r
+\r
+ #ifndef WIN32\r
+ command->killed = 0;\r
+ command->execlp_errno = 0;\r
+ #endif\r
+\r
+\r
+ return command;\r
+}\r
+\r
+int\r
+command_run(command_t command)\r
+{\r
+ if(!silent_flag && !interrupted)\r
+ INFO2("[%s] %s",command->context->pos, command->context->command_line);\r
+\r
+ if(!just_print_flag)\r
+ {\r
+ if(!interrupted)\r
+ {\r
+ /* start the command in a thread*/\r
+ if(command->context->async)\r
+ {\r
+ command->thread = xbt_os_thread_create("", command_start, command);\r
+\r
+ }\r
+ else\r
+ {\r
+ /* start the command in the main thread */\r
+ command_start(command);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ command_interrupt(command);\r
+ }\r
+\r
+\r
+ }\r
+\r
+ return 0;\r
+\r
+}\r
+\r
+static void*\r
+command_start(void* p)\r
+{\r
+ command_t command = (command_t)p;\r
+ unit_t root = command->root;\r
+\r
+ /* the command is started */\r
+ command->status = cs_started;\r
+\r
+ /* increment the number of started commands of the unit */\r
+ xbt_os_mutex_acquire(command->mutex);\r
+ (root->started_cmd_nb)++;\r
+ xbt_os_mutex_release(command->mutex);\r
+\r
+ /* execute the command of the test */\r
+\r
+ #ifndef WIN32\r
+ command_exec(command, command->context->command_line);\r
+ #else\r
+ /* play the translated command line on Windows */\r
+ command_exec(command, command->context->t_command_line);\r
+ #endif\r
+\r
+ if(cs_in_progress == command->status)\r
+ {\r
+ /* wait the process if it is in progress */\r
+ command_wait(command);\r
+\r
+ if(cs_failed != command->status && cs_interrupted != command->status)\r
+ command_check(command);\r
+ }\r
+\r
+ xbt_os_mutex_acquire(command->mutex);\r
+\r
+ /* if it's the last command of the root unit */\r
+ if(!root->interrupted && root->parsed && (root->started_cmd_nb == (root->failed_cmd_nb + root->interrupted_cmd_nb + root->successeded_cmd_nb)))\r
+ {\r
+ /* first release the mutex */\r
+ root->released = 1;\r
+ xbt_os_mutex_release(command->mutex);\r
+ /* the last command release the unit */\r
+ xbt_os_sem_release(root->sem);\r
+ }\r
+ else\r
+ xbt_os_mutex_release(command->mutex);\r
+\r
+\r
+ /* wait the end of the timer, the reader and the writer */\r
+ if(command->timer && command->timer->thread)\r
+ timer_wait(command->timer);\r
+\r
+ /* wait the end of the writer */\r
+ if(command->writer && command->writer->thread)\r
+ writer_wait(command->writer);\r
+\r
+ /* wait the end of the reader */\r
+ if(command->reader && command->reader->thread)\r
+ reader_wait(command->reader);\r
+\r
+\r
+\r
+ return NULL;\r
+}\r
+\r
+#ifdef WIN32\r
+\r
+#ifndef BUFSIZE\r
+#define BUFSIZE 4096\r
+#endif\r
+void\r
+command_exec(command_t command, const char* command_line)\r
+{\r
+\r
+ STARTUPINFO si = {0}; /* contains the informations about the child process windows*/\r
+ PROCESS_INFORMATION pi = {0}; /* contains child process informations */\r
+ SECURITY_ATTRIBUTES sa = {0}; /* contains the security descriptor for the pipe handles */\r
+ HANDLE child_stdin_handle[2] = {NULL}; /* child_stdin_handle[1] <-> stdout of the child process */\r
+ HANDLE child_stdout_handle[2] = {NULL}; /* child_stdout_handle[0] <-> stdin of the child process */\r
+ HANDLE child_stderr = NULL;\r
+\r
+\r
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);\r
+ sa.lpSecurityDescriptor = NULL; /* use default security for the pipe handles */\r
+\r
+ sa.bInheritHandle = TRUE; /* the pipe handles can be inherited */\r
+\r
+ if(!CreatePipe(&(child_stdin_handle[0]),&(child_stdin_handle[1]),&sa,0))\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));\r
+\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+\r
+ command->failed = 1;\r
+ command->status = cs_failed;\r
+\r
+ return;\r
+ }\r
+\r
+\r
+ if(!DuplicateHandle(GetCurrentProcess(),(child_stdin_handle[1]),GetCurrentProcess(),&(child_stderr),0,TRUE,DUPLICATE_SAME_ACCESS))\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));\r
+\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+\r
+ CloseHandle(child_stdin_handle[0]);\r
+ CloseHandle(child_stdin_handle[1]);\r
+\r
+ command->failed = 1;\r
+ command->status = cs_failed;\r
+\r
+ return;\r
+ }\r
+\r
+ if(!CreatePipe(&(child_stdout_handle[0]),&(child_stdout_handle[1]),&sa,0))\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+\r
+ CloseHandle(child_stdout_handle[0]);\r
+ CloseHandle(child_stdout_handle[1]);\r
+ CloseHandle(child_stdin_handle[0]);\r
+ CloseHandle(child_stdin_handle[1]);\r
+\r
+ command->failed = 1;\r
+ command->status = cs_failed;\r
+\r
+ return;\r
+ }\r
+\r
+ /* Read handle for read operations on the child std output. */\r
+ if(!DuplicateHandle(GetCurrentProcess(),(child_stdin_handle[0]),GetCurrentProcess(),&(command->stdout_fd),0,FALSE, DUPLICATE_SAME_ACCESS))\r
+ {\r
+ CloseHandle(child_stdout_handle[0]);\r
+ CloseHandle(child_stdout_handle[1]);\r
+ CloseHandle(child_stdin_handle[0]);\r
+ CloseHandle(child_stdin_handle[1]);\r
+\r
+ command->failed = 1;\r
+ command->status = cs_failed;\r
+\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+\r
+ return;\r
+ }\r
+\r
+\r
+ /* Write handle for write operations on the child std input. */\r
+ if(!DuplicateHandle(GetCurrentProcess(),(child_stdout_handle[1]),GetCurrentProcess(),&(command->stdin_fd), 0,FALSE,DUPLICATE_SAME_ACCESS))\r
+ {\r
+ CloseHandle(child_stdout_handle[0]);\r
+ CloseHandle(child_stdout_handle[1]);\r
+ CloseHandle(child_stdin_handle[0]);\r
+ CloseHandle(child_stdin_handle[1]);\r
+\r
+ command->failed = 1;\r
+ command->status = cs_failed;\r
+\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));\r
+\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+\r
+ return;\r
+ }\r
+\r
+ CloseHandle(child_stdin_handle[0]);\r
+ CloseHandle(child_stdout_handle[1]);\r
+\r
+\r
+ if(command->timer)\r
+ {\r
+ /* launch the timer */\r
+ timer_time(command->timer);\r
+ }\r
+\r
+ if(command->reader)\r
+ {\r
+ /* launch the reader */\r
+ reader_read(command->reader);\r
+ }\r
+\r
+\r
+ if(command->writer)\r
+ {\r
+ /* launch the writer */\r
+ writer_write(command->writer);\r
+ }\r
+\r
+ /* if there is a reader wait for its starting */\r
+ if(command->reader)\r
+ xbt_os_sem_acquire(command->reader->started);\r
+\r
+ /* if there is a reader wait for its ending */\r
+ if(command->writer)\r
+ xbt_os_sem_acquire(command->writer->written);\r
+\r
+ /* if there is a reader wait for its starting */\r
+ if(command->timer)\r
+ xbt_os_sem_acquire(command->timer->started);\r
+\r
+ si.cb = sizeof(STARTUPINFO);\r
+\r
+ si.dwFlags |= STARTF_USESTDHANDLES;\r
+ si.hStdOutput = child_stdin_handle[1];\r
+ si.hStdInput = child_stdout_handle[0];\r
+ si.hStdError = child_stderr;\r
+\r
+ /* launch the process */\r
+ if(!CreateProcess(\r
+ NULL,\r
+ tow32cmd(command_line),\r
+ NULL,\r
+ NULL,\r
+ TRUE,\r
+ CREATE_NO_WINDOW,\r
+ NULL,\r
+ NULL,\r
+ &si,\r
+ &pi)\r
+ )\r
+ {\r
+\r
+ if(ERROR_FILE_NOT_FOUND == GetLastError())\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(ECMDNOTFOUND, 1));\r
+ unit_set_error(command->unit, ECMDNOTFOUND, 1, command->context->pos);\r
+ command_handle_failure(command, csr_command_not_found);\r
+ }\r
+ else\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string((int)GetLastError(), 0));\r
+\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+ command_handle_failure(command, csr_create_process_function_failure);\r
+ }\r
+\r
+ }\r
+ else\r
+ {\r
+ /* the command is running */\r
+ command->status = cs_in_progress;\r
+\r
+ /* save the pid of the command */\r
+ command->pid = pi.hProcess;\r
+\r
+ /* close non used thread handle */\r
+ CloseHandle(pi.hThread);\r
+\r
+ }\r
+\r
+\r
+ /* close non used handles */\r
+ CloseHandle(child_stdin_handle[1]);\r
+ CloseHandle(child_stdout_handle[0]);\r
+ CloseHandle(child_stderr);\r
+\r
+\r
+}\r
+#else\r
+void\r
+command_exec(command_t command, const char* command_line)\r
+{\r
+ int child_stdin_fd[2] ;\r
+ int child_stdout_fd[2];\r
+\r
+ #ifdef __CHKCMD\r
+ int rv = is_cmd(command->unit->runner->path, command->unit->runner->builtin, command_line);\r
+\r
+ if(rv != 0)\r
+ {\r
+\r
+ if(rv == EINVAL)\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(rv, 0));\r
+ unit_set_error(command->unit, rv, 0, command->context->pos);\r
+ }\r
+ else\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(rv, 1));\r
+ unit_set_error(command->unit, rv, 1, command->context->pos);\r
+ }\r
+\r
+ command_handle_failure(command, csr_command_not_found);\r
+\r
+ return;\r
+ }\r
+\r
+ #endif\r
+\r
+\r
+ if(command->writer)\r
+ {\r
+ if(pipe(child_stdin_fd))\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));\r
+\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+\r
+ command_handle_failure(command, csr_pipe_function_failed);\r
+\r
+\r
+\r
+ return;\r
+ }\r
+ }\r
+\r
+ if(command->reader)\r
+ {\r
+ if(pipe(child_stdout_fd))\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));\r
+\r
+ if(command->writer)\r
+ {\r
+ close(child_stdin_fd[0]);\r
+ close(child_stdin_fd[1]);\r
+ }\r
+\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+\r
+ command_handle_failure(command, csr_pipe_function_failed);\r
+\r
+ return;\r
+ }\r
+ }\r
+\r
+ if(command->writer)\r
+ {\r
+ if(fcntl(child_stdin_fd[1], F_SETFL, fcntl(child_stdin_fd[1], F_GETFL) | O_NONBLOCK) < 0)\r
+ {\r
+\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));\r
+\r
+ close(child_stdin_fd[0]);\r
+ close(child_stdin_fd[1]);\r
+\r
+ if(command->reader)\r
+ {\r
+ close(child_stdout_fd[0]);\r
+ close(child_stdout_fd[1]);\r
+ }\r
+\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+\r
+ command_handle_failure(command, csr_fcntl_function_failed);\r
+\r
+ return;\r
+ }\r
+ }\r
+\r
+ /* to write to the child stdin */\r
+ command->stdin_fd = child_stdin_fd[1];\r
+\r
+ /* to read from the child stdout */\r
+ command->stdout_fd = child_stdout_fd[0];\r
+\r
+ /* launch the reader if any*/\r
+ if(command->reader)\r
+ reader_read(command->reader);\r
+\r
+ /* launch the writer if any */\r
+ if(command->writer)\r
+ writer_write(command->writer);\r
+\r
+ /* launch the timer if any */\r
+ if(command->timer)\r
+ timer_time(command->timer);\r
+\r
+ /* if there is a reader wait for its starting */\r
+ if(command->reader)\r
+ xbt_os_sem_acquire(command->reader->started);\r
+\r
+ /* if there is a reader wait for its ending */\r
+ if(command->writer)\r
+ xbt_os_sem_acquire(command->writer->written);\r
+\r
+ /* if there is a reader wait for its starting */\r
+ if(command->timer)\r
+ xbt_os_sem_acquire(command->timer->started);\r
+\r
+ /* update the state of the command, assume it is in progress */\r
+ command->status = cs_in_progress;\r
+\r
+ command->pid= fork();\r
+\r
+ if(command->pid < 0)\r
+ {/* error */\r
+ if(command->writer)\r
+ {\r
+ close(child_stdin_fd[0]);\r
+ close(child_stdin_fd[1]);\r
+ }\r
+\r
+ if(command->reader)\r
+ {\r
+ close(child_stdout_fd[0]);\r
+ close(child_stdout_fd[1]);\r
+ }\r
+\r
+ ERROR2("[%s] Cannot fork the command `%s'", command->context->pos, command->context->command_line);\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+ command_handle_failure(command,csr_fork_function_failure);\r
+ }\r
+ else\r
+ {\r
+ if(command->pid)\r
+ {/* father */\r
+\r
+ /* close unused file descriptors */\r
+ if(command->writer)\r
+ close(child_stdin_fd[0]);\r
+\r
+ if(command->reader)\r
+ close(child_stdout_fd[1]);\r
+ }\r
+ else\r
+ {/* child */\r
+\r
+ /* close unused file descriptors */\r
+ if(command->writer)\r
+ close(child_stdin_fd[1]);\r
+\r
+ if(command->reader)\r
+ close(child_stdout_fd[0]);\r
+\r
+ if(command->writer)\r
+ {\r
+ /* redirect stdin to child_stdin_fd[0] (now fgets(), getchar() ... read from the pipe */\r
+ if(dup2(child_stdin_fd[0],STDIN_FILENO) < 0)\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));\r
+ command->unit->exit_code = errno;\r
+\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+ command_handle_failure(command,csr_dup2_function_failure);\r
+ }\r
+\r
+ /* close the unused file descriptor */\r
+ close(child_stdin_fd[0]);\r
+ }\r
+\r
+ if(command->reader)\r
+ {\r
+\r
+ /* redirect stdout and stderr to child_stdout_fd[1] (now printf(), perror()... write to the pipe */\r
+ if(dup2(child_stdout_fd[1],STDOUT_FILENO) < 0)\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));\r
+\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+ command_handle_failure(command, csr_dup2_function_failure);\r
+ }\r
+\r
+ if(dup2(child_stdout_fd[1], STDERR_FILENO) < 0)\r
+ {\r
+ ERROR3("[%s] `%s' : NOK (%s)", command->context->pos, command->context->command_line, error_to_string(errno, 0));\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+ command_handle_failure(command, csr_dup2_function_failure);\r
+ }\r
+\r
+ /* close the unused file descriptor */\r
+ close(child_stdout_fd[1]);\r
+ }\r
+\r
+ /* launch the command */\r
+ if(execlp("/bin/sh", "sh", "-c", command->context->command_line, NULL) < 0)\r
+ command->execlp_errno = errno;\r
+ }\r
+ }\r
+}\r
+#endif\r
+\r
+#ifdef WIN32\r
+void\r
+command_wait(command_t command)\r
+{\r
+ /* wait for the command terminaison */\r
+ DWORD rv;\r
+\r
+ if(WAIT_FAILED == WaitForSingleObject(command->pid, INFINITE))\r
+ {\r
+ ERROR2("[%s] Cannot wait for the child`%s'", command->context->pos, command->context->command_line);\r
+\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+\r
+ command_handle_failure(command, csr_wait_failure );\r
+ /* TODO : see for the interruption */\r
+ }\r
+ else\r
+ {\r
+ /* don't take care of the timer or the writer or the reader failue */\r
+ if(cs_failed != command->status && cs_interrupted != command->status)\r
+ {\r
+ if(!GetExitCodeProcess(command->pid,&rv))\r
+ {\r
+ ERROR2("[%s] Cannot get the exit code of the process `%s'",command->context->pos, command->context->command_line);\r
+\r
+ unit_set_error(command->unit, (int)GetLastError(), 0, command->context->pos);\r
+\r
+ command_handle_failure(command, csr_get_exit_code_process_function_failure );\r
+ }\r
+ else\r
+ command->stat_val = command->exit_code = rv;\r
+ }\r
+ }\r
+}\r
+#else\r
+void\r
+command_wait(command_t command)\r
+{\r
+ if(!command->execlp_errno)\r
+ {\r
+ /* let this thread wait for the child so that the main thread can detect the timeout without blocking on the wait */\r
+ int pid = waitpid(command->pid, &(command->stat_val), 0);\r
+\r
+ if(pid != command->pid)\r
+ {\r
+ ERROR2("[%s] Cannot wait for the child`%s'", command->context->pos, command->context->command_line);\r
+\r
+ unit_set_error(command->unit, errno, 0, command->context->pos);\r
+\r
+ command_handle_failure(command, csr_waitpid_function_failure);\r
+ }\r
+ else\r
+ {\r
+ if(WIFEXITED(command->stat_val))\r
+ command->exit_code = WEXITSTATUS(command->stat_val);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ ERROR2("[%s] Cannot execute the command `%s'", command->context->pos, command->context->command_line);\r
+\r
+ unit_set_error(command->unit, command->execlp_errno, 0, command->context->pos);\r
+\r
+ command_handle_failure(command, csr_execlp_function_failure);\r
+ }\r
+}\r
+#endif\r
+\r
+void\r
+command_check(command_t command)\r
+{\r
+ int success = 1;\r
+ cs_reason_t reason;\r
+\r
+ /* we have a signal, store it */\r
+ if(WIFSIGNALED(command->stat_val))\r
+ {\r
+ command->signal = strdup(signal_name(WTERMSIG(command->stat_val),command->context->signal));\r
+ }\r
+\r
+ /* we have a signal and no signal is expected */\r
+ if(WIFSIGNALED(command->stat_val) && !command->context->signal)\r
+ {\r
+ success = 0;\r
+ ERROR3("[%s] `%s' : NOK (unexpected signal `%s' caught)", command->context->pos, command->context->command_line, command->signal);\r
+\r
+ unit_set_error(command->unit, EUNXPSIG, 1, command->context->pos);\r
+\r
+ reason = csr_unexpected_signal_caught;\r
+ }\r
+\r
+ /* we have a signal that differ form the expected signal */\r
+ if(WIFSIGNALED(command->stat_val) && command->context->signal && strcmp(signal_name(WTERMSIG(command->stat_val),command->context->signal),command->context->signal))\r
+ {\r
+\r
+ ERROR4("[%s] `%s' : NOK (got signal `%s' instead of `%s')", command->context->pos, command->context->command_line, command->signal, command->context->signal);\r
+\r
+ if(success)\r
+ {\r
+ success = 0;\r
+ unit_set_error(command->unit, ESIGNOTMATCH, 1, command->context->pos);\r
+ }\r
+\r
+ reason = csr_signals_dont_match;\r
+ }\r
+\r
+ /* we don't receive the expected signal */\r
+ if(!WIFSIGNALED(command->stat_val) && command->context->signal)\r
+ {\r
+\r
+ ERROR3("[%s] `%s' : NOK (expected `%s' not received)", command->context->pos, command->context->command_line, command->context->signal);\r
+\r
+ if(success)\r
+ {\r
+ success = 0;\r
+ unit_set_error(command->unit, ESIGNOTRECEIVED, 1, command->context->pos);\r
+ }\r
+\r
+ reason = csr_expected_signal_not_received;\r
+ }\r
+\r
+ /* if the command exit normaly and we expect a exit code : test it */\r
+ if(WIFEXITED(command->stat_val) /* && INDEFINITE != command->context->exit_code*/)\r
+ {\r
+ /* the exit codes don't match */\r
+ if(WEXITSTATUS(command->stat_val) != command->context->exit_code)\r
+ {\r
+ ERROR4("[%s] %s : NOK (returned code `%d' instead `%d')", command->context->pos, command->context->command_line, WEXITSTATUS(command->stat_val), command->context->exit_code);\r
+\r
+ if(success)\r
+ {\r
+ success = 0;\r
+ unit_set_error(command->unit, EEXITCODENOTMATCH, 1, command->context->pos);\r
+ }\r
+\r
+ reason = csr_exit_codes_dont_match;\r
+ }\r
+ }\r
+\r
+ /* make sure the reader done */\r
+ while(!command->reader->done)\r
+ xbt_os_thread_yield();\r
+\r
+ #ifdef WIN32\r
+ CloseHandle(command->stdout_fd);\r
+ #else\r
+ close(command->stdout_fd);\r
+ #endif\r
+\r
+ command->stdout_fd = INDEFINITE_FD;\r
+\r
+ xbt_strbuff_chomp(command->output);\r
+ xbt_strbuff_chomp(command->context->output);\r
+ xbt_strbuff_trim(command->output);\r
+ xbt_strbuff_trim(command->context->output);\r
+\r
+ if(!success && !strcmp(command->output->data, command->context->output->data))\r
+ {\r
+ xbt_dynar_t a = xbt_str_split(command->output->data, "\n");\r
+ char *out = xbt_str_join(a,"\n||");\r
+ xbt_dynar_free(&a);\r
+ INFO2("Output of <%s> so far: \n||%s", command->context->pos,out);\r
+ free(out);\r
+ }\r
+ /* if ouput handling flag is specified check the output */\r
+ else if(oh_check == command->context->output_handling && command->reader)\r
+ {\r
+ if(command->output->used != command->context->output->used || strcmp(command->output->data, command->context->output->data))\r
+ {\r
+ char *diff;\r
+\r
+\r
+ ERROR2("[%s] `%s' : NOK (outputs mismatch):", command->context->pos, command->context->command_line);\r
+\r
+ if(success)\r
+ {\r
+ unit_set_error(command->unit, EOUTPUTNOTMATCH, 1, command->context->pos);\r
+ success = 0;\r
+ }\r
+\r
+ reason = csr_outputs_dont_match;\r
+\r
+ /* display the diff */\r
+ diff = xbt_str_diff(command->context->output->data,command->output->data);\r
+ INFO1("%s",diff);\r
+ free(diff);\r
+ }\r
+ }\r
+ else if (oh_ignore == command->context->output_handling)\r
+ {\r
+ INFO1("(ignoring the output of <%s> as requested)",command->context->line);\r
+ }\r
+ else if (oh_display == command->context->output_handling)\r
+ {\r
+ xbt_dynar_t a = xbt_str_split(command->output->data, "\n");\r
+ char *out = xbt_str_join(a,"\n||");\r
+ xbt_dynar_free(&a);\r
+ INFO3("[%s] Here is the (ignored) command `%s' output: \n||%s",command->context->pos, command->context->command_line, out);\r
+ free(out);\r
+ }\r
+\r
+ if(success)\r
+ {\r
+ xbt_os_mutex_acquire(command->mutex);\r
+\r
+ if(command->status != cs_interrupted)\r
+ {\r
+ /* signal the success of the command */\r
+ command->status = cs_successeded;\r
+ command->successeded = 1;\r
+\r
+ /* increment the number of successeded command of the unit */\r
+ (command->root->successeded_cmd_nb)++;\r
+ }\r
+\r
+ xbt_os_mutex_release(command->mutex);\r
+ }\r
+ else\r
+ {\r
+ command_handle_failure(command, reason);\r
+ }\r
+}\r
+\r
+#ifdef WIN32\r
+void\r
+command_kill(command_t command)\r
+{\r
+ if(INDEFINITE_PID != command->pid)\r
+ {\r
+ INFO2("[%s] Kill the process `%s'", command->context->pos, command->context->command_line);\r
+ TerminateProcess(command->pid, INDEFINITE);\r
+ }\r
+}\r
+#else\r
+void\r
+command_kill(command_t command)\r
+{\r
+ if(INDEFINITE_PID != command->pid)\r
+ {\r
+ kill(command->pid,SIGTERM);\r
+\r
+ if(!command->context->signal)\r
+ command->context->signal = strdup("SIGTERM");\r
+\r
+ command->exit_code = INDEFINITE;\r
+ command->killed = 1;\r
+\r
+ usleep(100);\r
+\r
+ INFO2("[%s] Kill the process `%s'", command->context->pos, command->context->command_line);\r
+ kill(command->pid,SIGKILL);\r
+\r
+\r
+ }\r
+}\r
+#endif\r
+\r
+void\r
+command_interrupt(command_t command)\r
+{\r
+ xbt_os_mutex_acquire(command->mutex);\r
+\r
+ if((command->status != cs_interrupted) && (command->status != cs_failed) && (command->status != cs_successeded))\r
+ {\r
+ command->status = cs_interrupted;\r
+ command->reason = csr_interruption_request;\r
+ command->interrupted = 1;\r
+ command->unit->interrupted = 1;\r
+\r
+ xbt_os_mutex_acquire(command->root->mutex);\r
+ (command->root->interrupted_cmd_nb)++;\r
+ xbt_os_mutex_release(command->root->mutex);\r
+\r
+ if(command->pid != INDEFINITE_PID)\r
+ command_kill(command);\r
+ }\r
+\r
+ xbt_os_mutex_release(command->mutex);\r
+\r
+\r
+}\r
+\r
+void\r
+command_summarize(command_t command)\r
+{\r
+ if(cs_successeded != command->status)\r
+ {\r
+\r
+ #ifndef WIN32\r
+ if(command->killed)\r
+ printf(" <killed command>\n");\r
+ #endif\r
+\r
+ /* display the reason of the status of the command */\r
+ switch(command->reason)\r
+ {\r
+ /* the function pipe or CreatePipe() fails */\r
+ case csr_pipe_function_failed :\r
+ printf(" reason : pipe() or CreatePipe() function failed (system error)\n");\r
+ break;\r
+\r
+ case csr_shell_failed :\r
+ printf(" reason : shell failed (may be command not found)\n");\r
+ break;\r
+\r
+ case csr_get_exit_code_process_function_failure :\r
+ printf(" reason : ExitCodeProcess() function failed (system error)\n");\r
+ break;\r
+\r
+ /* reader failure reasons*/\r
+ case csr_read_pipe_broken :\r
+ printf(" reason : command read pipe broken\n");\r
+ break;\r
+\r
+ case csr_read_failure :\r
+ printf(" reason : command stdout read failed\n");\r
+ break;\r
+\r
+ /* writer failure reasons */\r
+ case csr_write_failure :\r
+ printf(" reason : command stdin write failed\n");\r
+ break;\r
+\r
+ case csr_write_pipe_broken :\r
+ printf(" reason : command write pipe broken\n");\r
+ break;\r
+\r
+ /* timer reason */\r
+ case csr_timeout :\r
+ printf(" reason : command timeouted\n");\r
+ break;\r
+\r
+ /* command failure reason */\r
+ case csr_command_not_found :\r
+ printf(" reason : command not found\n");\r
+ break;\r
+\r
+ /* context failure reasons */\r
+ case csr_exit_codes_dont_match :\r
+ printf(" reason : exit codes don't match\n");\r
+\r
+ break;\r
+\r
+ /* dup2 function failure reasons */\r
+ case csr_dup2_function_failure :\r
+ printf(" reason : dup2() function failed\n");\r
+\r
+ break;\r
+\r
+ /* execlp function failure reasons */\r
+ case csr_execlp_function_failure :\r
+ printf(" reason : execlp() function failed\n");\r
+\r
+ break;\r
+\r
+ /* waitpid function failure reasons */\r
+ case csr_waitpid_function_failure :\r
+ printf(" reason : waitpid() function failed\n");\r
+\r
+ break;\r
+\r
+ /* CreateProcess function failure reasons */\r
+ case csr_create_process_function_failure :\r
+ printf(" reason : CreateProcesss() function failed\n");\r
+\r
+ break;\r
+\r
+ case csr_outputs_dont_match :\r
+ {\r
+ /*char *diff;*/\r
+ printf(" reason : ouputs don't match\n");\r
+ /*diff = xbt_str_diff(command->context->output->data,command->output->data);\r
+ printf(" output diff :\n%s\n",diff);\r
+ free(diff);*/\r
+ }\r
+\r
+ break;\r
+\r
+ case csr_signals_dont_match :\r
+ printf(" reason : signals don't match\n");\r
+ break;\r
+\r
+ case csr_unexpected_signal_caught:\r
+ printf(" reason : unexpected signal caught\n");\r
+ break;\r
+\r
+ case csr_expected_signal_not_received :\r
+ printf(" reason : expected signal not receipt\n");\r
+ break;\r
+\r
+ /* system failure reasons */\r
+ case csr_fork_function_failure :\r
+ printf(" reason : fork function failed\n");\r
+ break;\r
+\r
+ case csr_wait_failure :\r
+ printf(" reason : wait command failure\n");\r
+ break;\r
+\r
+ /* global/local interruption */\r
+ case csr_interruption_request :\r
+ printf(" reason : the command receive a interruption request\n");\r
+ break;\r
+\r
+ /* unknown ? */\r
+ case csr_unknown :\r
+ printf(" reason : unknown \n");\r
+ }\r
+ }\r
+\r
+ if(csr_command_not_found != command->reason && csr_fork_function_failure != command->reason && csr_execlp_function_failure != command->reason)\r
+ {\r
+ if(INDEFINITE != command->exit_code)\r
+ /* the command exit code */\r
+ printf(" exit code : %d\n",command->exit_code);\r
+\r
+ /* if an expected exit code was specified display it */\r
+ if(INDEFINITE != command->context->exit_code)\r
+ printf(" expected exit code : %d\n",command->context->exit_code);\r
+ else\r
+ printf(" no expected exit code specified\n");\r
+\r
+ /* no expected signal expected */\r
+ if(NULL == command->context->signal)\r
+ {\r
+ printf(" no expected signal specified\n");\r
+\r
+ if(command->signal)\r
+ printf(" but got signal : %s\n",command->signal);\r
+\r
+ }\r
+ /* if an expected exit code was specified display it */\r
+ else\r
+ {\r
+ if(NULL != command->signal)\r
+ printf(" signal : %s\n",command->signal);\r
+ else\r
+ printf(" no signal caugth\n");\r
+ }\r
+\r
+ /* if the command has out put and the metacommand display output is specified display it */\r
+ if(command->output && (0 != command->output->used) && (oh_display == command->context->output_handling))\r
+ {\r
+ xbt_dynar_t a = xbt_str_split(command->output->data, "\n");\r
+ char *out = xbt_str_join(a,"\n||");\r
+ xbt_dynar_free(&a);\r
+ printf(" output :\n||%s",out);\r
+ free(out);\r
+ }\r
+ }\r
+\r
+ printf("\n");\r
+}\r
+\r
+void\r
+command_handle_failure(command_t command, cs_reason_t reason)\r
+{\r
+ unit_t root = command->root;\r
+\r
+ xbt_os_mutex_acquire(command->mutex);\r
+\r
+ if((command->status != cs_interrupted) && (command->status != cs_failed))\r
+ {\r
+ command->status = cs_failed;\r
+ command->reason = reason;\r
+ command->failed = 1;\r
+\r
+ command->unit->failed = 1;\r
+\r
+ xbt_os_mutex_acquire(root->mutex);\r
+\r
+ /* increment the number of failed command of the unit */\r
+ root->failed_cmd_nb++;\r
+\r
+ /* if the --ignore-failures option is not specified */\r
+ if(!keep_going_unit_flag)\r
+ {\r
+ if(!root->interrupted)\r
+ {\r
+ /* the unit interrupted (exit for the loop) */\r
+ root->interrupted = 1;\r
+\r
+ /* release the unit */\r
+ xbt_os_sem_release(root->sem);\r
+ }\r
+\r
+ /* if the --keep-going option is not specified */\r
+ if(!keep_going_flag)\r
+ {\r
+ if(!interrupted)\r
+ {\r
+ /* request an global interruption by the runner */\r
+ interrupted = 1;\r
+\r
+ /* release the runner */\r
+ xbt_os_sem_release(units_sem);\r
+ }\r
+ }\r
+ }\r
+\r
+ xbt_os_mutex_release(root->mutex);\r
+ }\r
+\r
+ xbt_os_mutex_release(command->mutex);\r
+}\r
+\r
+int\r
+command_free(command_t* ptr)\r
+{\r
+ /* close the stdin and the stdout pipe handles */\r
+\r
+ #ifdef WIN32\r
+ if((*ptr)->stdin_fd != INDEFINITE_FD)\r
+ CloseHandle((*ptr)->stdin_fd);\r
+\r
+ if((*ptr)->stdout_fd != INDEFINITE_FD)\r
+ CloseHandle((*ptr)->stdout_fd);\r
+\r
+ #else\r
+\r
+ if((*ptr)->stdin_fd != INDEFINITE_FD)\r
+ close((*ptr)->stdin_fd);\r
+\r
+ if((*ptr)->stdout_fd != INDEFINITE_FD)\r
+ close((*ptr)->stdout_fd);\r
+ #endif\r
+\r
+ if((*ptr)->timer)\r
+ {\r
+ if(timer_free(&((*ptr)->timer)) < 0)\r
+ return -1;\r
+ }\r
+\r
+ if((*ptr)->writer)\r
+ {\r
+ if(writer_free(&((*ptr)->writer)) < 0)\r
+ return -1;\r
+ }\r
+\r
+ if((*ptr)->reader)\r
+ {\r
+ if(reader_free(&((*ptr)->reader)) < 0)\r
+ return -1;\r
+ }\r
+\r
+ if((*ptr)->output)\r
+ xbt_strbuff_free((*ptr)->output);\r
+\r
+ if((*ptr)->context)\r
+ {\r
+ if(context_free(&((*ptr)->context)) < 0)\r
+ return -1;\r
+ }\r
+\r
+ if((*ptr)->signal)\r
+ free((*ptr)->signal);\r
+\r
+ free(*ptr);\r
+\r
+ *ptr = NULL;\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+\r