/* * guinnessd * architecture clients/serveur guinness * Thomas Nemeth -- le 15 juin 2001 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; setlocale (LC_ALL, ""); install_handler (); /* Valeurs par defaut */ logfile = stdout; outerr = stderr; /* 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; }