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_TASK_MANAGER := tests/test_task_manager
|
||||
TEST_TOOL_REGISTRY := tests/test_tool_registry
|
||||
TEST_TOOL_PROCESS := tests/test_tool_process
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
|
|
@ -156,6 +157,12 @@ $(TEST_TOOL_REGISTRY): \
|
|||
src/core/tool_registry.c
|
||||
$(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_NODE) \
|
||||
$(TEST_TREE_MODEL) \
|
||||
|
|
@ -170,7 +177,8 @@ test: \
|
|||
$(TEST_INVESTIGATION_SESSION) \
|
||||
$(TEST_BACKGROUND_TASK) \
|
||||
$(TEST_TASK_MANAGER) \
|
||||
$(TEST_TOOL_REGISTRY)
|
||||
$(TEST_TOOL_REGISTRY) \
|
||||
$(TEST_TOOL_PROCESS)
|
||||
@echo "Exécution des tests..."
|
||||
@./$(TEST_NODE)
|
||||
@./$(TEST_TREE_MODEL)
|
||||
|
|
@ -186,6 +194,7 @@ test: \
|
|||
@$(TEST_BACKGROUND_TASK)
|
||||
@$(TEST_TASK_MANAGER)
|
||||
@$(TEST_TOOL_REGISTRY)
|
||||
@$(TEST_TOOL_PROCESS)
|
||||
@echo "Tous les tests sont valides."
|
||||
|
||||
%.o: %.c
|
||||
|
|
@ -208,7 +217,8 @@ clean:
|
|||
$(TEST_INVESTIGATION_DAO) \
|
||||
$(TEST_INVESTIGATION_SESSION) \
|
||||
$(TEST_BACKGROUND_TASK) \
|
||||
$(TEST_TASK_MANAGER) \
|
||||
$(TEST_TOOL_REGISTRY)
|
||||
$(TEST_TASK_MANAGER) \
|
||||
$(TEST_TOOL_REGISTRY) \
|
||||
$(TEST_TOOL_PROCESS)
|
||||
|
||||
.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