172 lines
5.9 KiB
C
172 lines
5.9 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <signal.h>
|
||
|
#include <sys/wait.h>
|
||
|
|
||
|
#include "common.h"
|
||
|
|
||
|
|
||
|
int RUN = 1; // booléen pour la boucle d'acceptation
|
||
|
|
||
|
/* Définir la structure ici en global permet d'intialiser toutes les autres
|
||
|
* valeurs à 0.
|
||
|
* Quand le port est à 0 (comme ici), c'est le kernel qui l'attribue.
|
||
|
* */
|
||
|
struct sockaddr_in Sin = {AF_INET};
|
||
|
|
||
|
/* Fonction qui s'occupe d'un client après sa connexion */
|
||
|
void service(int);
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
int sfd, new_sfd; // socket id = file descriptor
|
||
|
struct sockaddr_in Srec; // on définit juste, les valeurs seront récuperées dans accept()
|
||
|
pid_t pid;
|
||
|
long lg; // pour récuperer la longueur de l'adresse dans accept()
|
||
|
|
||
|
/* Récupération des éventuels signaux qu'on pourrait recevoir, et appel de la fonction interrupt() */
|
||
|
signal(SIGCHLD, interrupt);
|
||
|
signal(SIGINT, interrupt);
|
||
|
signal(SIGTERM, interrupt);
|
||
|
|
||
|
/* Si on veut utiliser un port connu : host to network short : décommenter la ligne suivante */
|
||
|
Sin.sin_port = htons(PORT); // port dans l'ordre des octets réseau
|
||
|
Sin.sin_addr.s_addr = makeip4(127, 0, 0, 1);
|
||
|
// Sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||
|
|
||
|
/* Fabrication de l'interface réseau en IPv4 */
|
||
|
if ((sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
|
||
|
perror("socket");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* Puis faire un bind() pour s'attacher à ce port */
|
||
|
/* La fonction bind est une fonction unique, aveugle à la structure qui va envoyer les données. Elle ne se soucie
|
||
|
* pas de savoir si on va communiquer en IPv4 ou IPv6 par exemple.
|
||
|
* La fonction bind prend donc en fait une structure générique, sockaddr. Dans cette structure elle lit sa_family,
|
||
|
* qui lui indique qu'il s'agit d'IPv4. */
|
||
|
/* Pour faire la conversion des struct on fait un cast. */
|
||
|
if (bind(sfd, (struct sockaddr *) &Sin, sizeof(Sin)) == -1) {
|
||
|
perror("bind");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* On récupere le port d'attache */
|
||
|
lg = sizeof(Sin);
|
||
|
if (getsockname(sfd, (struct sockaddr *) &Sin, (socklen_t *) &lg) < 0) {
|
||
|
perror("getsockname");
|
||
|
return EXIT_FAILURE;
|
||
|
} else {
|
||
|
printf("Connexion de %s !\n", ip4tostring(ntohl(makeip4(127, 0, 0, 1))));
|
||
|
printf("Le serveur est attaché au port %d\n", ntohs(Sin.sin_port));
|
||
|
}
|
||
|
|
||
|
/* On définit le nombre d'écoutes simultanées */
|
||
|
if (listen(sfd, 10) == -1) {
|
||
|
perror("listen");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* On laisse le serveur accepter des connexions
|
||
|
* A chaque connexion établie, le serveur crée un fils pour gérer celle-ci. */
|
||
|
while (RUN) {
|
||
|
if ((new_sfd = accept(sfd, (struct sockaddr *) &Srec, (socklen_t *) &lg)) < 0) {
|
||
|
perror("accept");
|
||
|
} else {
|
||
|
printf("Connexion de %s !\n", ip4tostring(ntohl(Srec.sin_addr.s_addr)));
|
||
|
|
||
|
/* Création d'un processus dédié au client */
|
||
|
if ((pid = fork()) == -1) {
|
||
|
perror("fork");
|
||
|
write(new_sfd, "Erreur serveur !\n", 18);
|
||
|
} else {
|
||
|
if (pid == 0) { // code du fils
|
||
|
service(new_sfd); // Regroupe le code pour le fils
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
close(new_sfd); // On ferme le file descriptor qu'on vient de créer puisque c'est le fils qui s'en occupe
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
void service(int fd)
|
||
|
{
|
||
|
int i, n, t;
|
||
|
pid_t pid2;
|
||
|
char buf[LBUF], buf2[LBUF], eot = EOT;
|
||
|
void *memory = NULL;
|
||
|
char **command_line;
|
||
|
char delim[4];
|
||
|
char *token, *text;
|
||
|
|
||
|
delim[0] = ' ';
|
||
|
delim[1] = '\t';
|
||
|
delim[2] = '\n';
|
||
|
delim[3] = '\0';
|
||
|
|
||
|
while (1) {
|
||
|
/* Lecture du répertoire */
|
||
|
if ((n = readline(fd, buf, LBUF)) == -1) {
|
||
|
sprintf(buf, "Erreur réception message : sans doute trop long !\n");
|
||
|
write(fd, buf, strlen(buf) + 1);
|
||
|
} else {
|
||
|
/* Exécution de ls vers le pipe */
|
||
|
if ((pid2 = fork()) == -1) {
|
||
|
sprintf(buf, "Erreur système ! Revenez plus tard !\n");
|
||
|
write(fd, buf, strlen(buf) + 1);
|
||
|
close(fd);
|
||
|
return;
|
||
|
} else {
|
||
|
if (pid2 == 0) { // code du fils qui execute la commande
|
||
|
/* Il a dans buf la commande a executer */
|
||
|
|
||
|
/* Redirection de stdout et stderr vers le socket */
|
||
|
dup2(fd, STDOUT_FILENO);
|
||
|
dup2(fd, STDERR_FILENO);
|
||
|
|
||
|
strcpy(buf2, buf);
|
||
|
text = buf;
|
||
|
t = 0;
|
||
|
while ((token = strtok_r(text, delim, &text)) != NULL) {
|
||
|
t++;
|
||
|
}
|
||
|
t++; // Pour ajouter le NULL à la fin
|
||
|
|
||
|
if ((memory = malloc(t * sizeof(memory))) == NULL) {
|
||
|
sprintf(buf, "Erreur mémoire ! Revenez plus tard !\n");
|
||
|
write(fd, buf, strlen(buf) + 1);
|
||
|
close(fd);
|
||
|
return;
|
||
|
}
|
||
|
command_line = (char**) memory;
|
||
|
|
||
|
text = buf2;
|
||
|
for (i = 0; ((token = strtok_r(text, delim, &text)) != NULL); i++) {
|
||
|
command_line[i] = token;
|
||
|
}
|
||
|
command_line[i] = NULL;
|
||
|
|
||
|
execvp(command_line[0], command_line);
|
||
|
|
||
|
/* Si le execvp a marché le code suivant ne sera jamais executé.
|
||
|
* Pas besoin donc de faire un test */
|
||
|
perror("execvp");
|
||
|
exit(1);
|
||
|
}
|
||
|
/* Suite du pere */
|
||
|
wait(NULL); // Attend la fin du fils
|
||
|
write(fd, &eot, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
close(fd);
|
||
|
}
|