123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586 |
- /*
- * guinnessd
- * architecture clients/serveur guinness
- * Thomas Nemeth -- le 15 juin 2001
- *
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <time.h>
- #include <errno.h>
- #include <signal.h>
- #include <pthread.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <string.h>
- #include <strings.h>
- #include <locale.h>
- #include "defines.h"
- #include "xmem.h"
- #include "tools.h"
- #include "printlog.h"
- #include "lists.h"
- #include "broadcast.h"
- #include "drinks.h"
- #include "clients.h"
- #include "guinnessd.h"
- #include "commands.h"
- #include "config.h"
-
- /* Config specifique serveur */
- char *adr_ip = NULL;
- int port = 0;
- int online = TRUE;
- char *fichierlog = NULL;
- char *fichiercfg = NULL;
- char *chemin = NULL;
- char *admin_passwd = NULL;
- FILE *logfile = NULL;
- FILE *outerr = NULL;
- Elt *clients_list = NULL;
- Elt *drinks_list = NULL;
-
- /* Config specifique connexion des clients */
- char *pseudo = NULL;
- char *drink = NULL;
- char *logout = NULL;
-
-
- #ifdef SunOS
- char *crypt(const char *key, const char *salt);
- #else
- extern char *crypt __P ((__const char *__key, __const char *__salt));
- #endif
-
-
- void install_handler ();
-
-
- /*
- * Gestionnaire de signal SIGPIPE, SIGTERM, SIGQUIT et SIGINT
- *
- */
- void handler_signaux (int sig) {
- switch (sig) {
- case SIGPIPE:
- printlog (LOG_NOTIFY, "Signal SIGPIPE recu...\n");
- install_handler ();
- break;
- case SIGTERM:
- case SIGQUIT:
- case SIGINT:
- online = FALSE;
- printlog (LOG_NOTIFY, "Signal de terminaison recu...\n");
- break;
- default:
- printlog (LOG_NOTIFY, "Signal recu...\n");
- }
- }
-
-
- /*
- * Lecture du fichier de config : recuperation des valeurs
- *
- */
- char *get_string_from_token (char *line) {
- char *result = line;
- char *tmp;
- int keyval = 0;
-
- while ( (keyval != 1) && (result [0] != 0) ) {
- if (result [0] == '=') keyval = 1;
- if (keyval == 0) result++;
- }
- tmp = result++;
- while (tmp [0] != 0) {
- if (tmp [0] == '\n') tmp [0] = 0;
- tmp++;
- }
- return result;
- }
-
-
- /*
- * Lecture du fichier de config
- *
- */
- void load_config () {
- FILE *file;
- char tmpstr[MAXSTRLEN + 1];
- char *value;
-
- /* Ouverture du fichier */
- if ( (file = fopen (fichiercfg?fichiercfg:CONFIG_FILE, "r") ) != NULL) {
- /* Lecture du fichier */
- while (! feof (file) ) {
- memset (tmpstr, 0, MAXSTRLEN + 1);
- fgets (tmpstr, (int) MAXSTRLEN, file);
- value = get_string_from_token (tmpstr);
- if ( (strncmp (tmpstr, "port=", 5) == 0) && (port == 0) )
- port = atoi (value);
- if ( (strncmp (tmpstr, "passwd=", 7) == 0) &&
- (admin_passwd == NULL) )
- admin_passwd = xstrdup (crypt (value, "Gs") + 2);
- if ( (strncmp (tmpstr, "rep=", 4) == 0) && (chemin == NULL) )
- chemin = xstrdup (value);
- if ( (strncmp (tmpstr, "pseudo=", 7) == 0) && (pseudo == NULL) )
- pseudo = xstrdup (value);
- if ( (strncmp (tmpstr, "drink=", 6) == 0) && (drink == NULL) )
- drink = xstrdup (value);
- if ( (strncmp (tmpstr, "logout=", 7) == 0) && (logout == NULL) )
- logout = xstrdup (value);
- }
- fclose (file);
- #ifdef DEBUG
- } else {
- fprintf (stderr, "Pas de fichier de config (%s).\n", CONFIG_FILE);
- #endif
- }
- }
-
-
- /*
- * Aide
- *
- */
- void Usage (int help) {
- printf ("guinnessd by Thomas Nemeth - v %s (compilation %s : %s)\n",
- VERSION, OS_TYPE, COMPIL_DATE);
- if (help)
- printf ("Usage : guinnessd [-h] [-v] [-b] [-p port] "
- "[-s passwd] [-d chemin] [-l fichier] [-f fichier]\n"
- " -h : aide sur les parametres\n"
- " -v : affiche la version\n"
- " -b : detache le serveur du terminal\n"
- " -a adresse : specifie l'adresse du serveur\n"
- " -p port : specifie le numero du port\n"
- " -s passwd : specifie le mot de passe d'aministration\n"
- " -d chemin : indique le chemin ou se"
- " trouvent les ascii-arts\n"
- " -l fichier : fichier de log\n"
- " -f fichier : fichier de configuration\n\n");
- exit (1);
- }
-
-
- /*
- * Traitement des arguments
- *
- */
- int traite_argv (int argc, char *argv[]) {
- int option;
- int detach = FALSE;
-
- /* Verification des parametres */
- while ((option = getopt (argc, argv, "hvba:p:s:d:l:f:")) != -1) {
- switch (option) {
- case 'h' :
- Usage (TRUE);
- break;
- case 'v' :
- Usage (FALSE);
- break;
- case 'b' :
- detach = TRUE;
- break;
- case 'a' :
- adr_ip = optarg;
- break;
- case 'p' :
- port = atoi (optarg);
- break;
- case 's' :
- admin_passwd = xstrdup (crypt (optarg, "Gs") + 2);
- break;
- case 'd' :
- if (! chemin) {
- if (optarg[0] == '/') {
- chemin = xstrdup (optarg);
- } else {
- char *Pwd;
- Pwd = xmalloc ((MAXSTRLEN + 1) * sizeof (char));
- getcwd (Pwd, MAXSTRLEN);
- chemin = xmalloc ( (strlen (Pwd) + strlen (optarg) + 2)
- * sizeof (char) );
- snprintf (chemin, MAXSTRLEN, "%s/%s",
- Pwd, optarg);
- }
- }
- break;
- case 'l' :
- if (! fichierlog) {
- fichierlog = xstrdup (optarg);
- }
- break;
- case 'f' :
- if (! fichiercfg) {
- fichiercfg = xstrdup (optarg);
- }
- break;
- default:
- Usage (TRUE);
- break;
- }
- }
- if (optind < argc) {
- if (argc - optind == 1)
- fprintf (stderr, "%s: option inconnue --", argv[0]);
- else
- fprintf (stderr, "%s: options inconnues --", argv[0]);
- for ( ; optind < argc ; optind++)
- fprintf (stderr, " %s", argv[optind]);
- fprintf (stderr, "\n");
- Usage (TRUE);
- }
-
- return detach;
- }
-
-
- /*
- * Fonction initiant la connexion.
- *
- */
- int initiate (int socket_service, userinfos *infos) {
- int cont = TRUE;
- char userdatas[MAXSTRLEN];
- char *datas, *token, *saveptr;
- char delim[] = "\n\r";
- char *nom_distant = NULL;
- char *ip_distant = NULL;
- int port_distant = 0;
- int port_local = 0;
- time_t now;
- struct tm *today;
-
- memset (userdatas, 0, MAXSTRLEN);
-
- /* Recuperation des infos sur le connecte */
- get_sock_infos (socket_service,
- &nom_distant, &ip_distant,
- &port_local, &port_distant);
- if (ip_distant) {
- snprintf (infos->ip, MAXSTRLEN, "%s", ip_distant);
- free (ip_distant);
- } else sprintf (infos->ip, "0.0.0.0");
- if (nom_distant) {
- snprintf (infos->host, MAXSTRLEN, "%s", nom_distant);
- free (nom_distant);
- } else sprintf (infos->host, "none");
- infos->port = port_distant;
-
- printlog (LOG_NOTIFY, "Connexion entrante : %s %s\n",
- infos->ip, infos->host ? infos->host : "");
- printlog (LOG_NOTIFY, "Ports (loc/dist) : %d / %d\n",
- port_local, infos->port);
-
- /* PROTOCOLE DE CONNEXION */
- /* 1. envoi des commandes du serveur */
- if ( (cont = send_servercmds (socket_service)) == FALSE)
- return cont;
-
- /* 2. attente des donnees du connecte */
- if ( (cont = read_infos (socket_service, userdatas)) == FALSE)
- return cont;
-
- /* FIN DU PROTOCOLE : Vericifations / ajout dans la liste / affichage */
- datas = xstrdup (userdatas);
-
- /* Nom d'utilisateur */
- token = strtok_r (datas, delim, &saveptr);
- snprintf (infos->nom, MAXSTRLEN, "%s", token ? token : pseudo);
-
- /* Boisson preferee */
- token = strtok_r (NULL, delim, &saveptr);
- snprintf (infos->prefb, MAXSTRLEN, "%s", token ? token : drink);
-
- /* Message d'au-revoir */
- token = strtok_r (NULL, delim, &saveptr);
- snprintf (infos->logout, MAXSTRLEN, "%s", token ? token : logout);
-
- /* Date de connexion */
- time (&now);
- today = localtime (&now);
- strftime (infos->cxdate, MAXSTRLEN, "%a %d %b %Y %T", today);
-
- printlog (LOG_NOTIFY, "Utilisateur : [%s]\n", infos->nom);
- printlog (LOG_NOTIFY, "Boisson preferee : [%s]\n", infos->prefb);
- printlog (LOG_NOTIFY, "Message de logout : [%s]\n", infos->logout);
- printlog (LOG_NOTIFY, "Date de connexion : [%s]\n", infos->cxdate);
-
- pthread_mutex_lock (&mutex_clients);
- if (exist_elt (clients_list, infos->nom)) {
- char nick_ok[MAXSTRLEN+1];
- snprintf (nick_ok, MAXSTRLEN, "@%s", infos->nom);
- snprintf (infos->nom, MAXSTRLEN, "%s", nick_ok);
- send_infos (socket_service, "@Pseudo deja utilise !\n");
- printlog (LOG_NOTIFY, "Pseudo deje utilise => %s\n", infos->nom);
- pthread_mutex_unlock (&mutex_clients);
- return FALSE;
- }
- send_infos (socket_service, "Bienvenue sur le serveur de Guinness.\n");
- add_client (infos);
- pthread_mutex_unlock (&mutex_clients);
-
- if (! exist_elt (drinks_list, infos->prefb))
- send_infos (socket_service,
- "Votre boisson preferee n'est pas disponible sur ce"
- " serveur !\nVous aurez des Guinness a la place.\n");
-
- /* Passage en mode non bloquant */
- fcntl (socket_service, F_SETFL, O_NONBLOCK);
-
- return cont;
- }
-
-
- /*
- * Fonction de traitement de la connexion avec les clients
- *
- */
- void thread_service (int *sock_serv) {
- pthread_t tid = pthread_self ();
- long old_id = get_broadcastid ();
- int socket_service = (int) *sock_serv;
- int cont = TRUE;
- int nb;
- userinfos infos;
- char commande[MAXSTRLEN + 1];
-
- pthread_detach (tid);
-
- memset (infos.nom, 0, MAXSTRLEN + 1);
- memset (infos.prefb, 0, MAXSTRLEN + 1);
- memset (infos.logout, 0, MAXSTRLEN + 1);
- infos.admin = FALSE;
- infos.cold = FALSE;
-
- cont = initiate (socket_service, &infos);
-
- if (cont) {
- snprintf (commande, MAXSTRLEN,
- "%s a rejoint le serveur de Guinness.\n", infos.nom);
- broadcast (MESSAGE, NULL, commande);
- }
-
- #ifdef DEBUG
- printlog (LOG_NOTIFY, "Pret a recevoir des commandes.\n");
- #endif
-
- while (cont == TRUE) {
- memset (commande, 0, MAXSTRLEN + 1);
-
- /* Lecture des caracteres recus */
- do {
- sleep (1);
- nb = read (socket_service, commande, MAXSTRLEN);
- if ( (nb == -1) || (nb == 0) ) {
- if ( (errno == EAGAIN) && (nb == -1) ) continue;
- if ( (errno == EPIPE) || (nb == 0) ) {
- cont = FALSE;
- } else {
- printlog (LOG_ERROR, "Erreur de connexion reception !\n");
- }
- }
- } while ( (cont != FALSE) &&
- (nb < 0) &&
- (new_message (old_id) != TRUE) );
-
- #ifdef DEBUG
- printlog (LOG_NOTIFY, "Commande recue.\n");
- #endif
- if (cont == TRUE) {
- /* Traitement de la commande */
- if (nb > 0)
- cont = reply (socket_service, commande, &infos);
- /* Broadcast */
- if (new_message (old_id) == TRUE) {
- old_id = get_broadcastid ();
- cont = send_broadcast (socket_service, &infos);
- #ifdef DEBUG
- printlog (LOG_ERROR, "Emission broadcast pour %s (id = %ld)\n",
- infos.nom, old_id);
- #endif
- }
- }
- }
- printlog (LOG_NOTIFY, "Bye bye %s...\n", infos.nom);
-
- if (infos.nom[0] != '@') {
- pthread_mutex_lock (&mutex_clients);
- remove_client (&infos);
- pthread_mutex_unlock (&mutex_clients);
-
- broadcast (QUITTER, infos.nom, infos.logout);
- }
-
- close (socket_service);
- }
-
-
- /*
- * Installation du gestionnaire de signal SIGPIPE et autres...
- *
- */
- void install_handler () {
- signal (SIGPIPE, handler_signaux);
- signal (SIGTERM, handler_signaux);
- signal (SIGQUIT, handler_signaux);
- signal (SIGINT, handler_signaux);
- }
-
-
- /*
- * Initialisation generale
- *
- */
- void guinnessd_init (int argc, char *argv[]) {
- pthread_mutexattr_t mutex_attr;
- char *cptr;
-
- setlocale (LC_ALL, "");
- install_handler ();
-
- /* Valeurs par defaut */
- logfile = stdout;
- outerr = stderr;
-
- /* est-ce bien la place pour initialiser des trucs avant le parsing de
- la ligne de commande ? Eg: la variable d'environnement DRINKS_DIR
- */
-
- if (NULL!=(cptr=getenv("DRINKS_DIR"))) {
- chemin = xstrdup(cptr);
- }
-
-
- /* Traitement des parametres */
- if (traite_argv (argc, argv) == TRUE) {
- switch (fork()) {
- case -1: /* erreur */
- perror ("fork()");
- exit (-1);
- case 0: /* le fils */
- setsid ();
- break;
- default: /* le pere */
- exit (0);
- }
- }
-
- /* Lecture du fichier de configuration */
- load_config ();
-
- /* Initialisation des semaphores */
- pthread_mutexattr_init (&mutex_attr);
- pthread_mutex_init (&mutex_broadcast, &mutex_attr);
- pthread_mutex_init (&mutex_clients, &mutex_attr);
-
- /* Affectation des parametres */
- if (IS_NOT_GOOD (pseudo)) SET_STRING (pseudo, DEFAULT_PSEUDO);
- if (IS_NOT_GOOD (drink)) SET_STRING (drink, DEFAULT_DRINK);
- if (IS_NOT_GOOD (logout)) SET_STRING (logout, DEFAULT_LOGOUT);
- if (port == 0) port = DEFAULT_SERVER_PORT;
- if (fichierlog) {
- if ((logfile = fopen (fichierlog, "a"))) {
- outerr = logfile;
- } else {
- fprintf (stderr, "Impossible d'ouvrir le fichier %s : %s\n",
- fichierlog, strerror(errno));
- }
- }
-
- /* Option pour le buffer de logs */
- setvbuf (logfile, NULL, _IOLBF, 0);
-
- /* Creation de la liste de boissons par defaut*/
- add_elt (&drinks_list, "guinness");
-
- if (! chemin) chemin = xstrdup (DRINKS_DIR);
- drinks_list_files (chemin);
- drinks_display_list ();
- }
-
-
- /*
- * Fonction principale
- *
- */
- int main (int argc, char *argv[]) {
- int socket_ecoute;
- int socket_service;
- socklen_t lg_adresse = sizeof (struct sockaddr_in);
- struct sockaddr_in adresse;
- pthread_t pthread_id;
-
- guinnessd_init (argc, argv);
-
- /* Installation de l'ecoute du serveur */
- if ( (socket_ecoute = install_server (port, adr_ip, NULL)) == -1) {
- printlog (LOG_ERROR, "Impossible d'installer le service.\n");
- return -1;
- }
-
- /* Passage en mode non-bloquant */
- fcntl (socket_ecoute, F_SETFL, O_NONBLOCK);
-
- /* Boucle d'attente et d'ouverture de connexions */
- printlog (LOG_NOTIFY, "Serveur en attente de connexions (port %d)...\n",
- port);
- while (online == TRUE) {
- sleep (1);
-
- /* Attente d'une connexion */
- socket_service = accept (socket_ecoute, (struct sockaddr *) &adresse,
- &lg_adresse);
-
- /* SIGPIPE */
- if ( (socket_service == -1) && (errno == EINTR) ) continue;
-
- /* PAS DE CONNEXION => mode non bloquant pour attendre un ordre de
- * terminaison
- */
- if ( (socket_service == -1) && (errno == EAGAIN) ) continue;
-
- /* Autre erreur socket */
- if (socket_service == -1) {
- /* Erreur */
- printlog (LOG_ERROR,
- "Erreur d'acceptation de socket, errno = %d.\n",
- errno);
- perror (__FILE__ " accept");
- close (socket_ecoute);
- return -1;
- }
-
- /* Ici connexion acceptee */
- printlog (LOG_NOTIFY, "Connexion acceptee...\n");
-
- /* Lancement d'une activite de traitement */
- if (pthread_create (&pthread_id, NULL/*pthread_attr_default*/,
- (void *) thread_service,
- (void *) &socket_service) == -1) {
- printlog (LOG_ERROR, "Erreur creation thread de service.\n");
- close (socket_service);
- }
- /* fflush (logfile); */
- sleep (2);
- }
-
- printlog (LOG_NOTIFY, "Arret du serveur demande.\n");
- /* fflush (logfile); */
- close (socket_ecoute);
-
- free_list (&clients_list);
- free_list (&drinks_list);
-
- pthread_mutex_destroy (&mutex_broadcast);
- pthread_mutex_destroy (&mutex_clients);
-
- return 0;
- }
|