feat: add secure external tool execution
This commit is contained in:
parent
c0ef751434
commit
dc45abaf0d
7 changed files with 3340 additions and 3 deletions
16
Makefile
16
Makefile
|
|
@ -43,6 +43,7 @@ TEST_INVESTIGATION_SESSION := tests/test_investigation_session
|
||||||
TEST_BACKGROUND_TASK := tests/test_background_task
|
TEST_BACKGROUND_TASK := tests/test_background_task
|
||||||
TEST_TASK_MANAGER := tests/test_task_manager
|
TEST_TASK_MANAGER := tests/test_task_manager
|
||||||
TEST_TOOL_REGISTRY := tests/test_tool_registry
|
TEST_TOOL_REGISTRY := tests/test_tool_registry
|
||||||
|
TEST_TOOL_PROCESS := tests/test_tool_process
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
|
|
@ -156,6 +157,12 @@ $(TEST_TOOL_REGISTRY): \
|
||||||
src/core/tool_registry.c
|
src/core/tool_registry.c
|
||||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||||
|
|
||||||
|
$(TEST_TOOL_PROCESS): \
|
||||||
|
tests/test_tool_process.c \
|
||||||
|
src/core/tool_process.c
|
||||||
|
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||||
|
|
||||||
|
|
||||||
test: \
|
test: \
|
||||||
$(TEST_NODE) \
|
$(TEST_NODE) \
|
||||||
$(TEST_TREE_MODEL) \
|
$(TEST_TREE_MODEL) \
|
||||||
|
|
@ -170,7 +177,8 @@ test: \
|
||||||
$(TEST_INVESTIGATION_SESSION) \
|
$(TEST_INVESTIGATION_SESSION) \
|
||||||
$(TEST_BACKGROUND_TASK) \
|
$(TEST_BACKGROUND_TASK) \
|
||||||
$(TEST_TASK_MANAGER) \
|
$(TEST_TASK_MANAGER) \
|
||||||
$(TEST_TOOL_REGISTRY)
|
$(TEST_TOOL_REGISTRY) \
|
||||||
|
$(TEST_TOOL_PROCESS)
|
||||||
@echo "Exécution des tests..."
|
@echo "Exécution des tests..."
|
||||||
@./$(TEST_NODE)
|
@./$(TEST_NODE)
|
||||||
@./$(TEST_TREE_MODEL)
|
@./$(TEST_TREE_MODEL)
|
||||||
|
|
@ -186,6 +194,7 @@ test: \
|
||||||
@$(TEST_BACKGROUND_TASK)
|
@$(TEST_BACKGROUND_TASK)
|
||||||
@$(TEST_TASK_MANAGER)
|
@$(TEST_TASK_MANAGER)
|
||||||
@$(TEST_TOOL_REGISTRY)
|
@$(TEST_TOOL_REGISTRY)
|
||||||
|
@$(TEST_TOOL_PROCESS)
|
||||||
@echo "Tous les tests sont valides."
|
@echo "Tous les tests sont valides."
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|
@ -208,7 +217,8 @@ clean:
|
||||||
$(TEST_INVESTIGATION_DAO) \
|
$(TEST_INVESTIGATION_DAO) \
|
||||||
$(TEST_INVESTIGATION_SESSION) \
|
$(TEST_INVESTIGATION_SESSION) \
|
||||||
$(TEST_BACKGROUND_TASK) \
|
$(TEST_BACKGROUND_TASK) \
|
||||||
$(TEST_TASK_MANAGER) \
|
$(TEST_TASK_MANAGER) \
|
||||||
$(TEST_TOOL_REGISTRY)
|
$(TEST_TOOL_REGISTRY) \
|
||||||
|
$(TEST_TOOL_PROCESS)
|
||||||
|
|
||||||
.PHONY: clean run test
|
.PHONY: clean run test
|
||||||
|
|
|
||||||
1129
docs/tickets/closed/TICKET-037.md
Normal file
1129
docs/tickets/closed/TICKET-037.md
Normal file
File diff suppressed because it is too large
Load diff
189
include/core/tool_process.h
Normal file
189
include/core/tool_process.h
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file tool_process.h
|
||||||
|
* @brief Exécution sécurisée des outils externes.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LABFY_INVESTIGATION_TOOL_PROCESS_H
|
||||||
|
#define LABFY_INVESTIGATION_TOOL_PROCESS_H
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Erreurs produites pendant l’exécution d’un outil externe.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Un argument transmis à la fonction est invalide.
|
||||||
|
*/
|
||||||
|
TOOL_PROCESS_ERROR_INVALID_ARGUMENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Le processus n’a pas pu être créé ou lancé.
|
||||||
|
*/
|
||||||
|
TOOL_PROCESS_ERROR_SPAWN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* La communication avec le processus a échoué.
|
||||||
|
*/
|
||||||
|
TOOL_PROCESS_ERROR_COMMUNICATION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* L’exécution a été annulée.
|
||||||
|
*/
|
||||||
|
TOOL_PROCESS_ERROR_CANCELLED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Le processus s’est terminé sans produire un résultat exploitable.
|
||||||
|
*/
|
||||||
|
TOOL_PROCESS_ERROR_INVALID_RESULT
|
||||||
|
} ToolProcessError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Domaine d’erreur de ToolProcess.
|
||||||
|
*/
|
||||||
|
#define TOOL_PROCESS_ERROR \
|
||||||
|
tool_process_error_quark()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Résultat opaque d’une exécution externe.
|
||||||
|
*/
|
||||||
|
typedef struct ToolProcessResult ToolProcessResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retourne le domaine d’erreur de ToolProcess.
|
||||||
|
*
|
||||||
|
* @return Quark GLib du domaine d’erreur.
|
||||||
|
*/
|
||||||
|
GQuark tool_process_error_quark(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Exécute un outil externe sans passer par un shell.
|
||||||
|
*
|
||||||
|
* Le programme et chacun de ses arguments sont transmis séparément à
|
||||||
|
* GSubprocess.
|
||||||
|
*
|
||||||
|
* Un code de sortie différent de zéro ne constitue pas une erreur de cette
|
||||||
|
* fonction. Dans ce cas, la fonction retourne TRUE et le code de sortie est
|
||||||
|
* disponible dans ToolProcessResult.
|
||||||
|
*
|
||||||
|
* @param executable_path Chemin de l’exécutable.
|
||||||
|
* @param arguments Arguments supplémentaires terminés par NULL, ou NULL.
|
||||||
|
* @param working_directory Dossier de travail, ou NULL.
|
||||||
|
* @param cancellable Objet d’annulation facultatif.
|
||||||
|
* @param out_result Emplacement recevant le résultat.
|
||||||
|
* @param error Emplacement facultatif pour l’erreur.
|
||||||
|
*
|
||||||
|
* @return TRUE lorsqu’un résultat exploitable a été obtenu, sinon FALSE.
|
||||||
|
*/
|
||||||
|
gboolean tool_process_run(
|
||||||
|
const char *executable_path,
|
||||||
|
const char *const arguments[],
|
||||||
|
const char *working_directory,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
ToolProcessResult **out_result,
|
||||||
|
GError **error
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Libère un résultat d’exécution.
|
||||||
|
*
|
||||||
|
* @param result Résultat à libérer, ou NULL.
|
||||||
|
*/
|
||||||
|
void tool_process_result_free(
|
||||||
|
ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retourne une nouvelle référence sur la sortie standard.
|
||||||
|
*
|
||||||
|
* La référence retournée doit être libérée avec g_bytes_unref().
|
||||||
|
*
|
||||||
|
* @param result Résultat consulté.
|
||||||
|
*
|
||||||
|
* @return Nouvelle référence sur stdout, ou NULL.
|
||||||
|
*/
|
||||||
|
GBytes *tool_process_result_ref_stdout(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retourne une nouvelle référence sur la sortie d’erreur.
|
||||||
|
*
|
||||||
|
* La référence retournée doit être libérée avec g_bytes_unref().
|
||||||
|
*
|
||||||
|
* @param result Résultat consulté.
|
||||||
|
*
|
||||||
|
* @return Nouvelle référence sur stderr, ou NULL.
|
||||||
|
*/
|
||||||
|
GBytes *tool_process_result_ref_stderr(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indique si le processus s’est terminé normalement.
|
||||||
|
*
|
||||||
|
* @param result Résultat consulté.
|
||||||
|
*
|
||||||
|
* @return TRUE si le processus a appelé exit() ou retourné depuis main().
|
||||||
|
*/
|
||||||
|
gboolean tool_process_result_exited_normally(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retourne le code de sortie du processus.
|
||||||
|
*
|
||||||
|
* @param result Résultat consulté.
|
||||||
|
*
|
||||||
|
* @return Code de sortie, ou -1 si le processus ne s’est pas terminé
|
||||||
|
* normalement.
|
||||||
|
*/
|
||||||
|
int tool_process_result_get_exit_status(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indique si le processus a été terminé par un signal.
|
||||||
|
*
|
||||||
|
* @param result Résultat consulté.
|
||||||
|
*
|
||||||
|
* @return TRUE si le processus a été terminé par un signal.
|
||||||
|
*/
|
||||||
|
gboolean tool_process_result_was_signaled(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retourne le signal ayant terminé le processus.
|
||||||
|
*
|
||||||
|
* @param result Résultat consulté.
|
||||||
|
*
|
||||||
|
* @return Numéro du signal, ou zéro si aucun signal n’est disponible.
|
||||||
|
*/
|
||||||
|
int tool_process_result_get_termination_signal(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indique si le programme s’est terminé avec succès.
|
||||||
|
*
|
||||||
|
* Le succès fonctionnel signifie :
|
||||||
|
*
|
||||||
|
* - fin normale ;
|
||||||
|
* - code de sortie égal à zéro.
|
||||||
|
*
|
||||||
|
* @param result Résultat consulté.
|
||||||
|
*
|
||||||
|
* @return TRUE si le programme a retourné zéro.
|
||||||
|
*/
|
||||||
|
gboolean tool_process_result_is_success(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
612
src/core/tool_process.c
Normal file
612
src/core/tool_process.c
Normal file
|
|
@ -0,0 +1,612 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file tool_process.c
|
||||||
|
* @brief Exécution sécurisée des outils externes.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "core/tool_process.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct ToolProcessResult
|
||||||
|
* @brief Résultat interne d’une exécution externe.
|
||||||
|
*/
|
||||||
|
struct ToolProcessResult
|
||||||
|
{
|
||||||
|
GBytes *stdout_bytes;
|
||||||
|
GBytes *stderr_bytes;
|
||||||
|
|
||||||
|
gboolean exited_normally;
|
||||||
|
int exit_status;
|
||||||
|
|
||||||
|
gboolean was_signaled;
|
||||||
|
int termination_signal;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vérifie qu’une chaîne est définie et non vide.
|
||||||
|
*
|
||||||
|
* @param text Chaîne à vérifier.
|
||||||
|
*
|
||||||
|
* @return TRUE si la chaîne est valide.
|
||||||
|
*/
|
||||||
|
static gboolean tool_process_string_is_valid(
|
||||||
|
const char *text
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return text != NULL &&
|
||||||
|
text[0] != '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construit le vecteur argv transmis à GSubprocess.
|
||||||
|
*
|
||||||
|
* Les chaînes ne sont pas dupliquées. Le tableau les emprunte uniquement
|
||||||
|
* pendant la création du processus.
|
||||||
|
*
|
||||||
|
* @param executable_path Chemin de l’exécutable.
|
||||||
|
* @param arguments Arguments supplémentaires, ou NULL.
|
||||||
|
*
|
||||||
|
* @return Nouveau GPtrArray terminé par NULL.
|
||||||
|
*/
|
||||||
|
static GPtrArray *tool_process_build_argv(
|
||||||
|
const char *executable_path,
|
||||||
|
const char *const arguments[]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GPtrArray *argument_vector = NULL;
|
||||||
|
gsize argument_index = 0;
|
||||||
|
|
||||||
|
argument_vector = g_ptr_array_new();
|
||||||
|
|
||||||
|
g_ptr_array_add(
|
||||||
|
argument_vector,
|
||||||
|
(gpointer) executable_path
|
||||||
|
);
|
||||||
|
|
||||||
|
if (arguments != NULL)
|
||||||
|
{
|
||||||
|
for (argument_index = 0;
|
||||||
|
arguments[argument_index] != NULL;
|
||||||
|
argument_index++)
|
||||||
|
{
|
||||||
|
g_ptr_array_add(
|
||||||
|
argument_vector,
|
||||||
|
(gpointer) arguments[argument_index]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* argv doit obligatoirement se terminer par NULL.
|
||||||
|
*/
|
||||||
|
g_ptr_array_add(
|
||||||
|
argument_vector,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
return argument_vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Produit une erreur ToolProcess à partir d’une erreur interne.
|
||||||
|
*
|
||||||
|
* @param error Emplacement facultatif de l’erreur.
|
||||||
|
* @param error_code Code ToolProcess.
|
||||||
|
* @param context_message Description de l’opération ayant échoué.
|
||||||
|
* @param cause Erreur interne facultative.
|
||||||
|
*/
|
||||||
|
static void tool_process_set_wrapped_error(
|
||||||
|
GError **error,
|
||||||
|
ToolProcessError error_code,
|
||||||
|
const char *context_message,
|
||||||
|
const GError *cause
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *cause_message = NULL;
|
||||||
|
|
||||||
|
cause_message =
|
||||||
|
cause != NULL &&
|
||||||
|
cause->message != NULL
|
||||||
|
? cause->message
|
||||||
|
: "erreur inconnue";
|
||||||
|
|
||||||
|
g_set_error(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR,
|
||||||
|
error_code,
|
||||||
|
"%s : %s",
|
||||||
|
context_message,
|
||||||
|
cause_message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Force l’arrêt d’un processus puis attend sa terminaison.
|
||||||
|
*
|
||||||
|
* Cette fonction est utilisée après une annulation ou une erreur de
|
||||||
|
* communication afin de ne pas abandonner le processus enfant.
|
||||||
|
*
|
||||||
|
* @param subprocess Processus à arrêter.
|
||||||
|
*/
|
||||||
|
static void tool_process_force_exit_and_wait(
|
||||||
|
GSubprocess *subprocess
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GError *wait_error = NULL;
|
||||||
|
|
||||||
|
if (subprocess == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_subprocess_force_exit(
|
||||||
|
subprocess
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* L’attente ne doit pas utiliser le GCancellable déjà annulé.
|
||||||
|
*/
|
||||||
|
if (!g_subprocess_wait(
|
||||||
|
subprocess,
|
||||||
|
NULL,
|
||||||
|
&wait_error
|
||||||
|
))
|
||||||
|
{
|
||||||
|
g_clear_error(
|
||||||
|
&wait_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GQuark tool_process_error_quark(void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string(
|
||||||
|
"labfy-investigation-tool-process-error"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean tool_process_run(
|
||||||
|
const char *executable_path,
|
||||||
|
const char *const arguments[],
|
||||||
|
const char *working_directory,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
ToolProcessResult **out_result,
|
||||||
|
GError **error
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GPtrArray *argument_vector = NULL;
|
||||||
|
|
||||||
|
GSubprocessLauncher *launcher = NULL;
|
||||||
|
GSubprocess *subprocess = NULL;
|
||||||
|
|
||||||
|
GBytes *stdout_bytes = NULL;
|
||||||
|
GBytes *stderr_bytes = NULL;
|
||||||
|
|
||||||
|
ToolProcessResult *result = NULL;
|
||||||
|
|
||||||
|
GError *local_error = NULL;
|
||||||
|
|
||||||
|
GSubprocessFlags subprocess_flags =
|
||||||
|
G_SUBPROCESS_FLAGS_STDOUT_PIPE |
|
||||||
|
G_SUBPROCESS_FLAGS_STDERR_PIPE;
|
||||||
|
|
||||||
|
gboolean communication_success = FALSE;
|
||||||
|
|
||||||
|
g_return_val_if_fail(
|
||||||
|
error == NULL || *error == NULL,
|
||||||
|
FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tool_process_string_is_valid(
|
||||||
|
executable_path
|
||||||
|
) ||
|
||||||
|
out_result == NULL ||
|
||||||
|
*out_result != NULL ||
|
||||||
|
(working_directory != NULL &&
|
||||||
|
working_directory[0] == '\0'))
|
||||||
|
{
|
||||||
|
g_set_error_literal(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR,
|
||||||
|
TOOL_PROCESS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Les arguments fournis à ToolProcess sont invalides."
|
||||||
|
);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ne pas lancer un processus si l’opération est déjà annulée.
|
||||||
|
*/
|
||||||
|
if (cancellable != NULL &&
|
||||||
|
g_cancellable_is_cancelled(
|
||||||
|
cancellable
|
||||||
|
))
|
||||||
|
{
|
||||||
|
g_set_error_literal(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR,
|
||||||
|
TOOL_PROCESS_ERROR_CANCELLED,
|
||||||
|
"L’exécution de l’outil a été annulée avant son lancement."
|
||||||
|
);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
argument_vector = tool_process_build_argv(
|
||||||
|
executable_path,
|
||||||
|
arguments
|
||||||
|
);
|
||||||
|
|
||||||
|
if (argument_vector == NULL)
|
||||||
|
{
|
||||||
|
g_set_error_literal(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR,
|
||||||
|
TOOL_PROCESS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Le vecteur d’arguments n’a pas pu être construit."
|
||||||
|
);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
launcher = g_subprocess_launcher_new(
|
||||||
|
subprocess_flags
|
||||||
|
);
|
||||||
|
|
||||||
|
if (launcher == NULL)
|
||||||
|
{
|
||||||
|
g_ptr_array_unref(
|
||||||
|
argument_vector
|
||||||
|
);
|
||||||
|
|
||||||
|
g_set_error_literal(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR,
|
||||||
|
TOOL_PROCESS_ERROR_SPAWN,
|
||||||
|
"Le lanceur de processus n’a pas pu être créé."
|
||||||
|
);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (working_directory != NULL)
|
||||||
|
{
|
||||||
|
g_subprocess_launcher_set_cwd(
|
||||||
|
launcher,
|
||||||
|
working_directory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chaque argument est transmis séparément.
|
||||||
|
*
|
||||||
|
* Aucun shell n’interprète les espaces, étoiles, points-virgules
|
||||||
|
* ou substitutions présents dans les arguments.
|
||||||
|
*/
|
||||||
|
subprocess = g_subprocess_launcher_spawnv(
|
||||||
|
launcher,
|
||||||
|
(const char *const *) argument_vector->pdata,
|
||||||
|
&local_error
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_object(
|
||||||
|
&launcher
|
||||||
|
);
|
||||||
|
|
||||||
|
g_ptr_array_unref(
|
||||||
|
argument_vector
|
||||||
|
);
|
||||||
|
|
||||||
|
argument_vector = NULL;
|
||||||
|
|
||||||
|
if (subprocess == NULL)
|
||||||
|
{
|
||||||
|
tool_process_set_wrapped_error(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR_SPAWN,
|
||||||
|
"Impossible de lancer l’outil externe",
|
||||||
|
local_error
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_error(
|
||||||
|
&local_error
|
||||||
|
);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
communication_success = g_subprocess_communicate(
|
||||||
|
subprocess,
|
||||||
|
NULL,
|
||||||
|
cancellable,
|
||||||
|
&stdout_bytes,
|
||||||
|
&stderr_bytes,
|
||||||
|
&local_error
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!communication_success)
|
||||||
|
{
|
||||||
|
gboolean was_cancelled = FALSE;
|
||||||
|
|
||||||
|
was_cancelled =
|
||||||
|
(local_error != NULL &&
|
||||||
|
g_error_matches(
|
||||||
|
local_error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_CANCELLED
|
||||||
|
)) ||
|
||||||
|
(cancellable != NULL &&
|
||||||
|
g_cancellable_is_cancelled(
|
||||||
|
cancellable
|
||||||
|
));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* En cas d’erreur, GSubprocess ne garantit pas que les valeurs
|
||||||
|
* de sortie soient exploitables.
|
||||||
|
*/
|
||||||
|
g_clear_pointer(
|
||||||
|
&stdout_bytes,
|
||||||
|
g_bytes_unref
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_pointer(
|
||||||
|
&stderr_bytes,
|
||||||
|
g_bytes_unref
|
||||||
|
);
|
||||||
|
|
||||||
|
tool_process_force_exit_and_wait(
|
||||||
|
subprocess
|
||||||
|
);
|
||||||
|
|
||||||
|
if (was_cancelled)
|
||||||
|
{
|
||||||
|
tool_process_set_wrapped_error(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR_CANCELLED,
|
||||||
|
"L’exécution de l’outil a été annulée",
|
||||||
|
local_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tool_process_set_wrapped_error(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR_COMMUNICATION,
|
||||||
|
"La communication avec l’outil externe a échoué",
|
||||||
|
local_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_error(
|
||||||
|
&local_error
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_object(
|
||||||
|
&subprocess
|
||||||
|
);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* communicate() a réussi : le processus est terminé et son état
|
||||||
|
* peut maintenant être consulté.
|
||||||
|
*/
|
||||||
|
result = g_new0(
|
||||||
|
ToolProcessResult,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
if (stdout_bytes != NULL)
|
||||||
|
{
|
||||||
|
result->stdout_bytes =
|
||||||
|
stdout_bytes;
|
||||||
|
|
||||||
|
stdout_bytes = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result->stdout_bytes =
|
||||||
|
g_bytes_new_static(
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr_bytes != NULL)
|
||||||
|
{
|
||||||
|
result->stderr_bytes =
|
||||||
|
stderr_bytes;
|
||||||
|
|
||||||
|
stderr_bytes = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result->stderr_bytes =
|
||||||
|
g_bytes_new_static(
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
result->exited_normally =
|
||||||
|
g_subprocess_get_if_exited(
|
||||||
|
subprocess
|
||||||
|
);
|
||||||
|
|
||||||
|
result->was_signaled =
|
||||||
|
g_subprocess_get_if_signaled(
|
||||||
|
subprocess
|
||||||
|
);
|
||||||
|
|
||||||
|
result->exit_status = -1;
|
||||||
|
result->termination_signal = 0;
|
||||||
|
|
||||||
|
if (result->exited_normally)
|
||||||
|
{
|
||||||
|
result->exit_status =
|
||||||
|
g_subprocess_get_exit_status(
|
||||||
|
subprocess
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result->was_signaled)
|
||||||
|
{
|
||||||
|
result->termination_signal =
|
||||||
|
g_subprocess_get_term_sig(
|
||||||
|
subprocess
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Un processus terminé doit avoir soit quitté normalement,
|
||||||
|
* soit été terminé par un signal.
|
||||||
|
*/
|
||||||
|
if (!result->exited_normally &&
|
||||||
|
!result->was_signaled)
|
||||||
|
{
|
||||||
|
tool_process_result_free(
|
||||||
|
result
|
||||||
|
);
|
||||||
|
|
||||||
|
result = NULL;
|
||||||
|
|
||||||
|
g_clear_object(
|
||||||
|
&subprocess
|
||||||
|
);
|
||||||
|
|
||||||
|
g_set_error_literal(
|
||||||
|
error,
|
||||||
|
TOOL_PROCESS_ERROR,
|
||||||
|
TOOL_PROCESS_ERROR_INVALID_RESULT,
|
||||||
|
"Le processus s’est terminé dans un état non exploitable."
|
||||||
|
);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_object(
|
||||||
|
&subprocess
|
||||||
|
);
|
||||||
|
|
||||||
|
*out_result = result;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tool_process_result_free(
|
||||||
|
ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_pointer(
|
||||||
|
&result->stdout_bytes,
|
||||||
|
g_bytes_unref
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_pointer(
|
||||||
|
&result->stderr_bytes,
|
||||||
|
g_bytes_unref
|
||||||
|
);
|
||||||
|
|
||||||
|
g_free(
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GBytes *tool_process_result_ref_stdout(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL ||
|
||||||
|
result->stdout_bytes == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_bytes_ref(
|
||||||
|
result->stdout_bytes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GBytes *tool_process_result_ref_stderr(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL ||
|
||||||
|
result->stderr_bytes == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_bytes_ref(
|
||||||
|
result->stderr_bytes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean tool_process_result_exited_normally(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result->exited_normally;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tool_process_result_get_exit_status(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL ||
|
||||||
|
!result->exited_normally)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result->exit_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean tool_process_result_was_signaled(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result->was_signaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tool_process_result_get_termination_signal(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL ||
|
||||||
|
!result->was_signaled)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result->termination_signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean tool_process_result_is_success(
|
||||||
|
const ToolProcessResult *result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result->exited_normally &&
|
||||||
|
result->exit_status == 0;
|
||||||
|
}
|
||||||
BIN
tests/test_tool_process
Executable file
BIN
tests/test_tool_process
Executable file
Binary file not shown.
1397
tests/test_tool_process.c
Normal file
1397
tests/test_tool_process.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue