Question 1: à l'aide de la commande strace, lister les appels systèmes consécutifs vu en cours qui sont effectués par la commande cp lors de la copie d'un fichier. Noter aussi les paramètres de chaque appel système listé. Quelle est la taille du buffer de lecture et d'écriture de la commande cp ? Commande à lancer dans le terminal : strace -e trace=read,write,creat,open,close cp fichier_source fichier_destination D'après l'extrait strace suivant, le buffer de lecture/écriture fait visiblement 65536 octets. open("fichier_source", O_RDONLY) = 3 open("fichier_destination", O_WRONLY|O_TRUNC) = 4 read(3, "%PDF-1.5\n%\265\355\256\373\n3 0 obj\n<< /Lengt"..., 65536) = 65536 write(4, "%PDF-1.5\n%\265\355\256\373\n3 0 obj\n<< /Lengt"..., 65536) = 65536 read(3, "\305\212\306\"\362\247\325\305u\336\241\r\337X\330\341\247\302\260\224s\262\24\370D_\302\204/t\362;"..., 65536) = 65536 write(4, "\305\212\306\"\362\247\325\305u\336\241\r\337X\330\341\247\302\260\224s\262\24\370D_\302\204/t\362;"..., 65536) = 65536 read(3, "`c\260(A3K\376[\332?X\252m\31\316\30\236\220&yA\263\311\237aA<\26\315\233\373"..., 65536) = 65536 write(4, "`c\260(A3K\376[\332?X\252m\31\316\30\236\220&yA\263\311\237aA<\26\315\233\373"..., 65536) = 65536 read(3, "\22\342\34\265F\241Zj\322\353\264L6\22\303\26m\310\0206E\334q$\4/M)\356\4]:"..., 65536) = 4597 write(4, "\22\342\34\265F\241Zj\322\353\264L6\22\303\26m\310\0206E\334q$\4/M)\356\4]:"..., 4597) = 4597 Question 2: A l'aide des appels systèmes vus en cours, écrire en C les commandes my-ln, my-rm, my-mv, my-mkdir,my-rmdir qui prennent en argument un nom de fichier et qui respectivement créent un lien symbolique sur un fichier, effacent un fichier, renomment un fichier, créént un répertoire et effacent un répertoire. Pour la commande mv on ne devra utiliser que les appels système link() et unlink() (pas de rename) #include #include #include #include #include #include #include #define MAX_CMDS 5 #define LN 0 #define RM 1 #define MV 2 #define MKDIR 3 #define RMDIR 4 char *commandes[MAX_CMDS] = {"ln", "rm", "mv", "mkdir", "rmdir"}; void usage() { int i; printf("Usage : my where command is one of "); for(i=0;i \n"); exit(1); } if(link(argv[2],argv[3])<0) { perror("link"); exit(1); } break; case RM: if(argc<3) { printf("Usage : rm \n"); exit(1); } if(unlink(argv[2])<0) { perror("unlink"); exit(1); } break; case MV: if(argc<4) { printf("Usage : mv \n"); exit(1); } if(unlink(argv[3])<0) { if(errno!=ENOENT) { perror("unlink"); exit(1); } } if(link(argv[2],argv[3])<0) { perror("link"); exit(1); } if(unlink(argv[2])<0) { perror("unlink"); exit(1); } break; case MKDIR: if(argc<3) { printf("Usage : mkdir \n"); exit(1); } if(mkdir(argv[2],0777)<0) { perror("mkdir"); exit(1); } break; case RMDIR: if(argc<3) { printf("Usage : rmdir \n"); exit(1); } if(rmdir(argv[2])<0) { perror("rmdir"); exit(1); } break; default: printf("Unknown command\n"); } } Question 3: écrire en C la commande appelée moncat, qui ne prends aucun paramètre, mais qui : - Affiche sur la sortie standard tout ce qui est lu depuis l'entrée standard jusqu'à l'indication de fin de fichier indiquée par l'appel système read() - La commande doit tester si chaque opération de lecture/écriture a réussi. Si ce n'est pas le cas, elle affichera des détails sur l'erreur avec la fonction perror() de la librairie C. #include #define BUFSIZE 5 void main() { char buffer[BUFSIZE]; int n_read; do { n_read=read(0,buffer,BUFSIZE); if(n_read < 0) { perror("Erreur de lecture\n"); exit(1); } if((write(1,buffer,n_read)<0)) { perror("Erreur d'écriture\n"); exit(1); } }while(n_read!=0); } Question 4: Modifier moncat de la question 2 pour qu'il implémente l'option -n de cat. L'option -n préfixe chaque ligne d'un fichier par son numéro. Une ligne est définie par toute chaine de caractère qui se termine par un \n. Pour l'affichage sur le terminal, il n'est pas autorisé d'utiliser la fonction printf. Indication : utiliser un buffer intermédaire, strcat(3) et sprintf(3) avant d'afficher le résultat sur la sortie standard avec write(2). Exemple d'exécution : % cat -n ceci 1 ceci est 2 est un 3 un test 4 test #include #include #include #define BUFSIZE 5 void main(int argc, char *argv[]) { char buffer[BUFSIZE]; char buff_tmp[BUFSIZE]={0}; int n_read; int cmpt_li=1; bool first_pass = true; bool option = false; //Détection de l'option de num. de ligne if(argc > 1){ if(strcmp(argv[1],"-l")==0){ option = true; } else{ perror("Bad arguments <-l>\n"); exit(EXIT_FAILURE); } } if(option){ do { //Lit sur l'entrée standrard n_read=read(0,buffer,BUFSIZE); if(n_read < 0) { perror("Erreur de lecture\n"); exit(EXIT_FAILURE); } //Si c'est un début de ligne, on ajoute le num. de ligne if(first_pass){ sprintf(buff_tmp,"%d ",cmpt_li); if((write(1,buff_tmp,BUFSIZE)<0)) { perror("Erreur d'écriture\n"); exit(EXIT_FAILURE); } cmpt_li++; first_pass=false; } //Recherche d'un retour chariot for(int i=0; i ------ #!/bin/bash for i in $* do echo Fichier ----- <"$i"> ------ ./moncat < "$i" done Question 6: Modifier le code source C du programme moncat de manière à ce que : - Si un nom de fichier est donné en paramètre, il affiche le contenu du fichier - Si il y a une erreur à l'ouverture du fichier (par exemple si le fichier n'existe pas), le programme doit le signaler avec la fonction perror() de la librairie C. - Si aucun nom de fichier n'est donné, le fonctionnement reste le même qu'auparavant. #include #include #define BUFSIZE 5 void main(int argc, char *argv[]) { char buffer[BUFSIZE]; int n_read; int input_desc=0; if(argc > 1){ input_desc=open(argv[1],O_RDONLY); if(input_desc<0){ perror(argv[1]); exit(EXIT_FAILURE); } } do { n_read=read(input_desc,buffer,BUFSIZE); if(n_read < 0) { perror("Erreur de lecture\n"); exit(1); } if((write(1,buffer,n_read)<0)) { perror("Erreur d'écriture\n"); exit(1); } }while(n_read!=0); } Question 7: La commande unix head affiche les n premières lignes de l'entrée standard sur la sortie standard, avec n donné en paramètre. Si n n'est pas fourni, sa valeur par défaut est de 10. ré-écrire la commande monhead en C. Question 8: La commande unix wc affiche respectivement le nombre de lignes, mots et caractères lues depuis l'entrée standard sur la sortie standard. Les lignes sont séparées par des \n, les mots par des espaces et les caractères par rien. Ré-écrire la commande monwc en C. Question 9: La commande diff permet d'afficher les différences entre deux fichiers lignes par ligne. à l'aide des appels systèmes vus en cours, écrire en C une commande mondiff qui compare deux fichiers ligne par ligne et qui si il y a une différence entre deux même numéro de lignes, affiche les deux lignes concernées. Si deux lignes sont identiques, mondiff n'affiche rien. Question 10: A partir de moncat en C, écrire un script moncp.sh qui prends en argument deux paramètres $1 et $2 et qui crée un copie du fichier $1 dans le fichier $2. - Mesurer la performance de votre outil de copie moncp.sh en Ko/s et la comparer avec celle de l'outil cp fourni par le système. - D'où vient la différence de performance ? Indication : Utiliser strace pour observer les différences de paramètre sur les appels système read() et write() et en déduire les modifications nécessaires dans code source C de moncat pour améliorer les performances de votre outil dvotre outil dee copie. Question 11: Modifier le programme moncat écrit en question 2 pour qu'il redirige les messages d'erreurs de perror() dans un fichier appelé erreurs.log dans le répertoire d'exécution courant. Ce programme fera appel aux appels systèmes close() et dup() ou dup2(). Rappel : la fonction perror() de la librairie C écrit sur l'erreur standard d'un processus, dont le descripteur de fichier est le numéro 2. Question 12: Ajouter une option au programme moncat en C pour qu'il affiche le contenu de l'entrée standard ou d'un fichier dans l'ordre inverse. Ex: moncat -r bonjour ruojnob Question 13: La commande tee lit écrit tout ce qu'elle lit depuis l'entrée standard sur la sortie standard et dans un fichier donné en paramètre. écrire la commande tee en C à l'aide des appels système vus en cours. Question 14: La commande more permet d'afficher un fichier en faisant une pause toute les n lignes, afin de mettre à l'utilisateur de lire le contenu d'un fichier facilement sur un terminal. Modifier moncat en C pour qu'il fasse une pause dans l'affichage toutes les n lignes (par exemple en attendant l'entrée d'un caractère particulier) et émule donc le comportement de la commande more. Question 15: L'appel système lseek() permet de placer la tête de lecture/écriture d'un fichier au-delà de la fin actuelle du fichier, sans que cela modifie la taille réelle en nombre de blocs du fichiers. Si des données sont écrites à cet emplacement, une lecture ultérieure de l'espace intermédiaire (un "trou" ) retournera des zéro (\0) jusqu'à ce que d'autres données y soient écrite. Ecrire un programme "create_file " qui crée un fichier dans le chemin d'accès nom et dont la taille apparente est taille en octets. Vérifier avec les commandes ls et du que votre programme fonctionne comme attendu. Question 16: Ecrire une commande de gestion de log monlog qui fonctionne de la manière suivante : Elle reçoit des lignes de logs en entrée standard et préfixe ces lignes de logs par la date et l'heure du système (voir la page de manuel de section 3 de ctime pour savoir comment). Ensuite, elle écrit ces lignes de logs en remplissant un fichier initialement vide mais dont la taille devra être limitée par une option donnée en paramètre. Lorsque le fichier de log courant est plein, la commande monlog ouvre un autre fichier de logs vide et le remplit à son tour. Les noms de fichiers de logs sont suffixés par un numéro de séquence incrémental indiquant l'ordre d'apparition des logs. Par exemple le fichier log.1 est plus ancien que le fichier log.2 et si on effectue la commande cat log1 log2, on affiche l'ensemble des logs dans leur ordre d'apparition. Question 17: La commande moncat écrite en question 2 préfixait toute nouvelle ligne par son numéro. Ce programme est relativement simple à écrire si on suppose que la longueur d'une ligne ne dépasse jamais une certaine valeur : Le buffer utilisé pour stocker la ligne et son numéro a une taille fixe. Modifier moncat pour qu'il puisse fonctionner pour une longueur de ligne quelconque, y compris plus grande que celle d'un buffer initialement alloué. Indice : utiliser la fonction reaalloc() de la librairie C pour ré-allouer la taille d'une zone mémoire dynamiquement en fonction de la demande.