CSC 4103 – Programmation système

Portail informatique

Contrôle Final 1 – Année 2022/2023

Le barème est donné à titre indicatif

Echauffement (3 points)

Vous disposez de l'arborescence suivante:

libfoo/ foo.c foo.h src/ main.c bar.c bar.h

1 point

Donnez la ou les lignes de commandes à exécuter depuis le répertoire libfoo pour générer la bibliothèque partagée libfoo.so à partir du fichier foo.c
gcc -shared -o libfoo.so foo.c

2 points

Depuis le répertoire src, on exécute la commande gcc main.o bar.o -o main -lfoo pour générer l'exécutable main. Cette commande échoue avec le message d'erreur suivant:
$ gcc main.o bar.o -o main -lfoo /usr/bin/ld : ne peut pas trouver -lfoo : Aucun fichier ou dossier de ce type collect2: error: ld returned 1 exit status

Expliquez le problème et corrigez la commande permettant de générer l'executable main.

L'éditeur de lien ne trouve pas le fichier libfoo.so (ou libfoo.a). Pour corriger le problème, il faut indiquer l'emplacement de la bibliothèque avec l'option -L:
$ gcc main.o bar.o -o main -lfoo -L../libfoo

AskGPT (6 points)

ChatGPT est un outil de chat reposant sur de l'Intelligence Artificielle. ChatGPT est conçu pour générer du texte cohérent et pertinent en réponse à des requêtes humaines. Pour cet exercice, on souhaite implémenter la commande /askGPT question chargée d'interroger chatGPT depuis le chat que vous avez étudié lors de la séance 10. Par exemple:
(ornithorynque rusé) /askGPT Peux-tu faire l'examen de CSC4103 à ma place ? * chatGPT: Je suis désolé, mais en tant qu'IA, je ne peux pas passer d'examen à la place d'un étudiant, ni aider à la malhonnêteté académique. Mon but est d'offrir une assistance et un soutien éducatif légitime aux étudiants qui en ont besoin, mais je ne suis pas autorisé à participer à des activités malhonnêtes. Veuillez étudier dur et faire de votre mieux pour réussir votre examen.

2 points

On considère qu'on dispose de la fonction void chat_gpt(const char* question, char* reponse, size_t buffer_size) qui envoie la question question à chatGPT et recopie la réponse dans la zone mémoire reponse qui est de taille buffer_size. Modifiez la fonction void process_cmd_askGPT(client_t* client, char* param) pour:
  • allouer une zone mémoire de taille 2000 octets
  • appeler la fonction chatGPT, puis envoyer la réponse à tous les clients du chat
Veillez à éviter les fuites mémoire.
void process_cmd_askGPT(client_t* client, char* param) { char*message = param; char* reponse = malloc(2000); chat_gpt(message, reponse, 2000); send_message_all(reponse); free(reponse); }

3 points

Le serveur de chat permet de traiter les requêtes de nombreux clients de manière concurrente. La fonction chat_gpt ne peut toutefois pas traiter plus de 4 requêtes à la fois. Modifiez la fonction process_cmd_askGPT pour assurer que les appels à la fonction chat_gpt respectent cette contrainte.

Si besoin, indiquez les modifications à apporter aux fonctions void server_init() (appelée au lancement du serveur) et void server_finalize() (appelée à la terminaison du serveur).

void server_init() { memset(clients, 0, sizeof(client_t*)*MAX_CLIENTS); sem = sem_open("/chatGPT", O_CREAT, S_IRWXU, 4); } void server_finalize() { sem_close(sem); } void process_cmd_askGPT(client_t*client, char*param) { char* message = param; char* reponse = malloc(2000); sem_wait(sem); chat_gpt(message, reponse, 2000); sem_post(sem); send_message_all(reponse); free(reponse); }

Fortune (4 points)

Fortune est un programme qui affiche un message au hasard provenant d'une base de données de citations. Le but de cet exercice est d'implémenter une fonctionnalité similaire dans le chat via la commande /fortune n (où n est le numéro de la citation). Par exemple:
(Castor joyeux) /fortune 17 The only way to keep your health is to eat what you don't want, drink what you don't like, and do what you'd rather not. -- Mark Twain
Pour cela, vous disposez d'un fichier fortunes.dat dans lequel sont stockées les différentes citations (ou fortunes). Chaque citation est stockée à la suite de la précédente sous la forme d'un struct fortune:
struct fortune { char fortune_text [2000]; }

Implémentez la fonction void process_cmd_fortune(client_t* client, char*param). Cette fonction est invoquée par le serveur quand un utilisateur lance la commande /fortune n (où n est un entier). La fonction doit ouvrir le fichier fortunes.dat et lire la nième fortune (n=0 indiquant la première fortune). Si le numéro de fortune n'est pas valide (par exemple car il est supérieur au nombre de fortunes), la fonction doit envoyer le message "Invalid fortune Id" au client qui a fait la demande. Le fichier de fortune étant très grand, veillez à ne lire que la partie du fichier demandée par l'utilisateur.
Pour connaître la taille du fichier fortunes.dat, vous pouvez utiliser la fonction int get_file_size(const char* filename).
Il n'est pas demandé de traiter les erreurs autres que "Invalid fortune Id".
void process_cmd_fortune(client_t*client, char*param) { int fortune_id = atoi(param); int offset = fortune_id * sizeof(struct fortune); if(offset+sizeof(struct fortune) > get_file_size("fortunes.dat")) { send_message("Invalid fortune Id\n", client); } else { FILE* f = fopen("fortunes.dat", "r"); fseek(f, offset, SEEK_SET); struct fortune fortune; fread(&fortune, sizeof(fortune), 1, f); send_message_all(fortune.fortune_text); fclose(f); } }

Fortune_record (9 pt)

Afin de compléter la base de données utilisée par la commande /fortune, on souhaite implémenter la commande /fortune_record n qui enregistre les n derniers messages dans la base de données.

4 points

Pour enregistrer les n derniers messages, il est nécessaire de sauvegarder chaque message publié. Pour cela, modifiez la fonction void send_message_all(char *s) afin que chaque message publié soit ajouté dans la liste chaînée message_list définie à la ligne 16.
Pour simplifier cette question, il n'est pas demandé de traiter les cas où un message est plus grand que la zone mémoire char message[128].
/* Send message to all clients */ void send_message_all(char *s){ int i; for(i=0;i < MAX_CLIENTS;i++){ if(clients[i]){ send_message(s, clients[i]); } } struct message *msg = malloc(sizeof(struct message)); strncpy(msg->message, s, 128); msg->message[127] = '\0'; msg->prev = message_list; message_list = msg; }

3 points

Implémentez la fonction void process_cmd_fortune_record(client_t* client, char* param). Cette fonction est appelée quand un utilisateur lance la commande /fortune_record n (où n est un entier). Elle doit enregister les n derniers messages sous la forme d'une seule fortune dans le fichier fortunes.dat.

Cette fonction doit allouer un struct fortune et le remplir avec les n derniers messages envoyés (donc les n premiers éléments de la liste chaînée). Si la liste contient moins de n messages, la fortune est construite à partir de tous les messages de la liste.

Pour simplifier cette question, il n'est pas demandé de maintenir l'ordre chronologique des message dans la fortune: on souhaite uniquement enregistrer les n derniers messages, quelque soit leur ordre.
Pour concaténer les messages, vous pouvez utiliser la fonction char *strcat(char* dst, const char* src) qui ajoute la chaîne src à la fin de la chaîne dst.
Pour simplifier cette question, il n'est pas demandé de traiter les cas où les n messages sont plus grands que la zone mémoire char fortune_text[2000].
void process_cmd_fortune_record(client_t*client, char*param) { int n = atoi(param); struct fortune fortune; memset(fortune.fortune_text, 0, 2000); // make sure the buffer is filled with zeros struct message *msg = message_list; while(msg && n>0) { strcat(fortune.fortune_text, msg->message); msg = msg->prev; n--; } FILE* f = fopen("fortunes.dat", "a"); fwrite(&fortune, sizeof(fortune), 1, f); fclose(f); }

3 points

On souhaite implémenter la fonction void process_cmd_forget(client_t* client, char* param). Cette fonction est appelée lorsqu'un client lance la commande /forget nn est un entier. Cette fonction supprime de la liste chainée le nème élément afin qu'il ne puisse pas être sauvegardé. Implémentez cette fonctionnalité dans la fonction process_cmd_forget.
void process_cmd_forget(client_t* client, char* param) { int n = atoi(param); struct message *cur = message_list; if(n==0) { if(cur) { message_list = cur->prev; free(cur); } return; } struct message *next = cur->prev; while(next && n>1) { next = next->prev; cur = cur->prev; n--; } if(next) { cur->prev = next->prev; free(cur); } }