wm: khar

ref: c2ea79dba50215eaeefba1c4cffba868fea8c624
dir: /khar.c/

View raw version
#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);
	}
}