ref: c2ea79dba50215eaeefba1c4cffba868fea8c624
dir: /khar.c/
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <signal.h> #include "config.h" #define PORTNO "32723" #define BACKLOG 20 #define MAX_CLIENT 50 #define BUF_SIZE 1024 void atomic_decr(pthread_mutex_t *mutex, int *resource) { pthread_mutex_lock(mutex); *resource = *resource - 1; pthread_mutex_unlock(mutex); } void atomic_incr(pthread_mutex_t *mutex, int *resource) { pthread_mutex_lock(mutex); *resource = *resource + 1; pthread_mutex_unlock(mutex); } typedef struct { int sockfd; struct sockaddr_storage claddr; socklen_t claddr_len; } client_t; client_t clients[MAX_CLIENT]; int clients_count = 0; pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER; char * printable_addr(struct sockaddr * saddr, socklen_t saddr_len) { char host[NI_MAXHOST], serv[NI_MAXSERV], res[NI_MAXHOST+NI_MAXSERV + 2]; int s; s = getnameinfo(saddr, saddr_len, host, NI_MAXHOST, serv, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV); if (s != 0) { sprintf(res, "UNKNKOWN:UNKNOWN"); } else { sprintf(res, "%s:%s", host, serv); } return strdup(res); } int init_socket(int socktype) { struct addrinfo hints, *results, *rp; int sfd, status, opt = 1; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = socktype; hints.ai_flags = AI_PASSIVE; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; status = getaddrinfo(NULL, PORTNO, &hints, &results); if (status != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); exit(EXIT_FAILURE); } for (rp = results; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(sfd, BACKLOG) == 0) break; close(sfd); } freeaddrinfo(results); if (rp == NULL) { fprintf(stderr, "Could't bind\n"); exit(EXIT_FAILURE); } return sfd; } void write_all(char *msg, int length) { for (int i = 0; i < clients_count; i++) { if (clients[i].sockfd != -1) write(clients[i].sockfd, msg, length); } } void* handle_client(void *arg) { int index = *(int *)arg, s; char buffer[BUF_SIZE] = {0}, *a, *all_khar_msg; a = printable_addr((struct sockaddr*)&clients[index].claddr, clients[index].claddr_len); /* Print joined_message_public */ s = NI_MAXHOST + 5 + strlen(joined_message_public) + 2; all_khar_msg = (char *)malloc(s); if (all_khar_msg == NULL) { all_khar_msg = strdup(joined_message_public); } else { snprintf(all_khar_msg, s, joined_message_public, a); } write_all(all_khar_msg, strlen(all_khar_msg)); free(all_khar_msg); /* print join_message_private */ write(clients[index].sockfd, joined_message_private, strlen(joined_message_private)); while (read(clients[index].sockfd, buffer, BUF_SIZE)) { if (buffer[0] == '\n') continue; else if (strncmp(buffer, "/list", 5) == 0) { write(clients[index].sockfd, list_message, strlen(list_message)); for (int i = 0; i < MAX_CLIENT; i++) { if (clients[i].sockfd != -1) { char *addr = printable_addr((struct sockaddr*)&clients[i].claddr, clients[i].claddr_len); addr[strlen(addr) + 1] = '\n'; write(clients[index].sockfd, addr, strlen(addr) + 2); free(addr); } } memset(buffer, 0, BUF_SIZE); continue; } break; } s = NI_MAXHOST + 5 + strlen(message_template) + BUF_SIZE; all_khar_msg = (char *)malloc(s); if (all_khar_msg == NULL) { all_khar_msg = strdup(message_template); } else { snprintf(all_khar_msg, s, message_template, a, buffer); } write_all(all_khar_msg, strlen(all_khar_msg)); free(all_khar_msg); write(clients[index].sockfd, khar_message_private, strlen(khar_message_private)); close(clients[index].sockfd); // this need to be closed in server too clients[index].sockfd = -1; s = NI_MAXHOST + 5 + strlen(khar_message_public) + 2; all_khar_msg = (char *)malloc(s); if (all_khar_msg == NULL) { all_khar_msg = strdup(khar_message_public); } else { snprintf(all_khar_msg, s, khar_message_public, a); } write_all(all_khar_msg, strlen(all_khar_msg)); free(all_khar_msg); atomic_decr(&count_mutex, &clients_count); free(arg); pthread_detach(pthread_self()); } int main(int argc, char *argv[]) { int serverfd, cfd, *copy, free_index; struct addrinfo hints; struct sockaddr_storage claddr; pthread_t tid; socklen_t claddr_len; serverfd = init_socket(SOCK_STREAM); /* Returns a scoket or otherwise terminates the program */ signal(SIGPIPE, SIG_IGN); for (int i = 0; i < MAX_CLIENT; i++) clients[i].sockfd = -1; for (;;) { cfd = accept(serverfd, (struct sockaddr*)&claddr, &claddr_len); if (cfd == -1) continue; if (clients_count + 1 >= MAX_CLIENT) { char *a; a = printable_addr((struct sockaddr *)&claddr, claddr_len); fprintf(stderr, "giving up on client %s.\n", a); write(cfd, max_clients_private, strlen(max_clients_private)); close(cfd); free(a); continue; } for (int i = 0; i <= MAX_CLIENT; i++) { if (clients[i].sockfd == -1) { free_index = i; break; } } clients[free_index].claddr = claddr; clients[free_index].sockfd = cfd; clients[free_index].claddr_len = claddr_len; copy = (int *)malloc(sizeof(int)); if (copy == NULL) { write(cfd, khar_message_private, strlen(khar_message_private)); close(cfd); clients[free_index].sockfd = -1; continue; } *copy = free_index; printf("%s connected.\n", printable_addr((struct sockaddr *)&clients[free_index].claddr, clients[free_index].claddr_len)); pthread_create(&tid, NULL, handle_client, (void*) copy); atomic_incr(&count_mutex, &clients_count); } }