diff --git a/Secured/secured_transaction.c b/Secured/secured_transaction.c index 69a1ad6..f6352b4 100644 --- a/Secured/secured_transaction.c +++ b/Secured/secured_transaction.c @@ -2,7 +2,7 @@ #include <stdlib.h> #include <unistd.h> #include <fcntl.h> -#include <time.h> +#include <time.h> #include <sys/wait.h> #include <string.h> @@ -10,44 +10,111 @@ #define MAX_NAME_LEN 10 #define FILENAME "../Data/accounts.db" -// Fonction pour lire un compte dans le fichier avec verrouillage +// Couleurs pour l'affichage +#define RESET "\033[0m" +#define GREEN "\033[32m" +#define RED "\033[31m" +#define PINK "\033[35m" +#define BLUE "\033[34m" + + +/** + * @brief Lit les informations d'un compte à partir d'un fichier avec verrouillage. + * + * Cette fonction lit un compte spécifié par son index à partir du fichier + * spécifié par le descripteur de fichier. Elle place un verrou exclusif sur + * la section correspondante du fichier pour garantir qu'aucune autre + * opération ne puisse modifier le compte en même temps. Après la lecture, + * le verrou est libéré. + * + * @param fd Le descripteur de fichier du fichier contenant les comptes. + * @param index L'index du compte à lire (en termes de position dans le fichier). + * @param name Un pointeur vers une chaîne de caractères où le nom du compte sera stocké. + * @param balance Un pointeur vers un entier où le solde du compte sera stocké. + */ void read_account(int fd, int index, char *name, int *balance) { char buffer[ACCOUNT_SIZE]; // Placer un verrou exclusif sur la section du fichier correspondant au compte lockf(fd, F_LOCK, ACCOUNT_SIZE); + + // Se positionner à l'index du compte dans le fichier lseek(fd, index * ACCOUNT_SIZE, SEEK_SET); + + // Lire l'enregistrement du compte read(fd, buffer, ACCOUNT_SIZE); + + // Extraire le nom et le solde du compte sscanf(buffer, "%7s %d", name, balance); + // Libérer le verrou après lecture lockf(fd, F_ULOCK, ACCOUNT_SIZE); } -// Fonction pour écrire un compte dans le fichier avec verrouillage +/** + * @brief Écrit les informations d'un compte dans un fichier avec verrouillage. + * + * Cette fonction écrit un compte spécifié par son index dans le fichier + * spécifié par le descripteur de fichier. Avant d'écrire, elle formate les + * données du compte (nom et solde) pour les rendre compatibles avec la taille + * d'un enregistrement, puis place un verrou exclusif sur la section du fichier + * correspondante pour garantir que personne d'autre ne peut modifier ces + * données pendant l'écriture. Après l'écriture, le verrou est libéré. + * + * @param fd Le descripteur de fichier du fichier contenant les comptes. + * @param index L'index du compte à écrire (en termes de position dans le fichier). + * @param name Le nom du compte à écrire. + * @param balance Le solde du compte à écrire. + * + */ void write_account(int fd, int index, const char *name, int balance) { char buffer[ACCOUNT_SIZE]; - // Formater la chaîne avec un nom de 10 caractères et un solde de 5 caractères, espace rempli si nécessaire + // Formater la chaîne snprintf(buffer, ACCOUNT_SIZE, "%-7s%8d", name, balance); - // S'assurer que le buffer est exactement de taille ACCOUNT_SIZE + // S'assurer que le buffer est exactement de taille ACCOUNT_SIZE (16) for (int i = strlen(buffer); i < ACCOUNT_SIZE - 1; i++) { buffer[i] = ' '; } - buffer[ACCOUNT_SIZE - 1] = '\n'; // Assurez-vous que la ligne est terminée correctement + buffer[ACCOUNT_SIZE - 1] = '\n'; // Pour s'assurer que la ligne est terminée correctement (avec un saut de ligne) // Placer un verrou exclusif sur la section du fichier correspondant au compte lockf(fd, F_LOCK, ACCOUNT_SIZE); + + // Se positionner à l'index du compte dans le fichier lseek(fd, index * ACCOUNT_SIZE, SEEK_SET); + + // Écrire l'enregistrement du compte write(fd, buffer, ACCOUNT_SIZE); + // Libérer le verrou après écriture lockf(fd, F_ULOCK, ACCOUNT_SIZE); } -// Fonction pour effectuer une transaction avec verrouillage +/** + * @brief Effectue une transaction aléatoire entre deux comptes avec verrouillage. + * + * Cette fonction simule une transaction entre deux comptes distincts sélectionnés + * aléatoirement. Elle garantit la cohérence des données en utilisant des verrouillages + * sur les sections correspondantes du fichier pour lire et écrire les informations + * des comptes. Le montant transféré est calculé aléatoirement et ne dépasse pas + * 20% du solde du compte source. + * + * @param fd Le descripteur de fichier du fichier contenant les comptes. + * @param account_count Le nombre total de comptes dans le fichier. + * + * @note Les comptes source et destination sont choisis de manière aléatoire, + * mais un compte ne peut pas transférer à lui-même. + * @note La transaction est annulée si le solde du compte source est insuffisant + * pour effectuer un transfert minimal. + * + */ void perform_transaction(int fd, int account_count) { int from_idx = rand() % account_count; int to_idx = rand() % account_count; + + // Sélectionner un autre compte si source et destination sont identiques while (from_idx == to_idx) { to_idx = rand() % account_count; } @@ -55,7 +122,7 @@ void perform_transaction(int fd, int account_count) { char from_name[MAX_NAME_LEN], to_name[MAX_NAME_LEN]; int from_balance, to_balance; - // Lire les comptes source et destination + // Lire les informations des comptes source et destination read_account(fd, from_idx, from_name, &from_balance); read_account(fd, to_idx, to_name, &to_balance); @@ -72,60 +139,114 @@ void perform_transaction(int fd, int account_count) { write_account(fd, from_idx, from_name, from_balance); write_account(fd, to_idx, to_name, to_balance); - printf("Transaction : %s -> %s : %d€\n", from_name, to_name, transfer_amount); + // Afficher un message de transaction réussie + printf(GREEN "Transaction : %s -> %s : %d€\n" RESET, from_name, to_name, transfer_amount); } else { - printf("Transaction annulée : Fonds insufisants %s\n", from_name); + // Afficher un message d'annulation de transaction si le solde est insuffisant + printf(RED "Transaction annulée : Fonds insuffisants pour %s\n" RESET, from_name); } } -// Fonction pour un processus de transactions +/** + * @brief Effectue un ensemble de transactions dans un processus. + * + * Cette fonction ouvre le fichier contenant les comptes en mode lecture/écriture + * et effectue un nombre donné de transactions aléatoires. Chaque transaction est + * réalisée en appelant la fonction `perform_transaction`. Une temporisation aléatoire + * est introduite entre les transactions pour simuler un comportement réaliste. + * + * @param transactions Le nombre de transactions à effectuer. + * @param account_count Le nombre total de comptes dans le fichier. + * + * @note La fonction ouvre le fichier spécifié par `FILENAME` en mode lecture/écriture. + * Elle s'assure de le fermer correctement à la fin. + * + * @warning Si l'ouverture du fichier échoue, la fonction interrompt le programme + * avec un appel à `exit(1)`. + * + * @details + * - La temporisation introduite par `usleep` (avec une durée aléatoire) contribue à la simulation d'un trafic réseau ou de retards dans les transactions. + */ void process_transactions(int transactions, int account_count) { + // Ouvrir le fichier contenant les comptes en mode lecture/écriture int fd = open(FILENAME, O_RDWR); if (fd < 0) { perror("Erreur : Impossible d'ouvrir le fichier"); exit(1); } + // Effectuer le nombre spécifié de transactions for (int i = 0; i < transactions; i++) { perform_transaction(fd, account_count); - //usleep(rand() % 100000); // Temporisation aléatoire + + // Temporisation aléatoire pour simuler un délai + usleep(rand() % 100000); } + // Fermer le fichier après avoir terminé les transactions close(fd); } + +/** + * @brief Calcule le solde total de tous les comptes dans le fichier. + * + * Cette fonction parcourt tous les comptes stockés dans le fichier spécifié + * par `FILENAME`, en lisant leurs soldes et en les additionnant pour produire + * un solde total. Elle ouvre le fichier en mode lecture seule, le parcourt + * compte par compte, puis le ferme avant de retourner le résultat. + * + * @return Le solde total de tous les comptes, ou -1 en cas d'erreur + * (par exemple, si l'ouverture du fichier échoue). + * + * @note La taille de chaque compte est définie par la constante `ACCOUNT_SIZE`. + * + * @warning Cette fonction ne valide pas les données des comptes lues depuis le fichier. + * Elle suppose que les données sont correctement formatées. + * + */ int calculate_total_balance() { + // Ouvrir le fichier contenant les comptes en mode lecture seule int fd = open(FILENAME, O_RDONLY); if (fd < 0) { perror("Erreur : Impossible d'ouvrir le fichier"); return -1; } + // Calculer le nombre de comptes à partir de la taille du fichier off_t file_size = lseek(fd, 0, SEEK_END); int account_count = file_size / ACCOUNT_SIZE; + // Initialiser le solde total int total_balance = 0; char name[MAX_NAME_LEN]; int balance; + // Parcourir et additionner les soldes de chaque compte for (int i = 0; i < account_count; i++) { read_account(fd, i, name, &balance); total_balance += balance; } + // Fermer le fichier avant de retourner le résultat close(fd); return total_balance; } int main(int argc, char *argv[]) { + // Vérifie que deux arguments sont passés en paramètre if (argc != 3) { fprintf(stderr, "Usage: %s <number_of_processes> <transactions_per_process>\n", argv[0]); return 1; } + // Extraire le nombre de processus et le nombre de transactions par processus int num_processes = atoi(argv[1]); int transactions_per_process = atoi(argv[2]); + // Initialiser une graine aléatoire basée sur l'ID de processus et l'heure actuelle + // On aurait aussi pu utiliser la fonction rand() directement, mais rand_r() est plus sûr + // Car il permet d'éviter les interférences entre les processus unsigned int seed = getpid() ^ time(NULL); int random_value = rand_r(&seed); @@ -134,7 +255,7 @@ int main(int argc, char *argv[]) { if (initial_balance < 0) { return 1; // Erreur lors de l'ouverture du fichier } - printf("Solde total initial : %d€\n", initial_balance); + printf(PINK "Solde total initial : %10d€\n" RESET, initial_balance); // Ouvrir le fichier pour déterminer le nombre de comptes int fd = open(FILENAME, O_RDONLY); @@ -145,32 +266,37 @@ int main(int argc, char *argv[]) { off_t file_size = lseek(fd, 0, SEEK_END); close(fd); + // Calculer le nombre de comptes dans le fichier int account_count = file_size / ACCOUNT_SIZE; - printf("Nombre de comptes : %d\n", account_count); + printf(BLUE "Nombre de comptes : %d\n" RESET, account_count); - // Créer les processus enfants + // Créer les processus enfants pour effectuer les transactions for (int i = 0; i < num_processes; i++) { if (fork() == 0) { process_transactions(transactions_per_process, account_count); - exit(0); + exit(0); // Chaque processus enfant se termine ici } } - // Attendre les processus enfants + // Attendre que tous les processus enfants se terminent for (int i = 0; i < num_processes; i++) { wait(NULL); } - // Calcul du solde total final + // Calculer le solde total final après toutes les transactions int final_balance = calculate_total_balance(); if (final_balance < 0) { return 1; // Erreur lors de l'ouverture du fichier } - printf("Solde total final : %d€\n", final_balance); + printf(PINK "Solde total final : %10d€\n" RESET, final_balance); + + // Comparer le solde initial et le solde final if (initial_balance != final_balance) { - printf("Le solde final est différent du solde initial...\n"); + printf(RED "Le solde final est différent du solde initial...\n" RESET); + }else{ + printf(GREEN "Le solde final est identique au solde initial !\n" RESET); } printf("Tout les processus sont terminés !\n");