#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <signal.h>

#define DEF_PROC_NUM 2
#define PROC_NUM_LIMIT 128
#define UDP_PORT 54321

int server(int s)
{
	struct sockaddr_in remote_address;
	int r, len;

	#define BUF_SIZE 256
	char buf[BUF_SIZE];

	signal(SIGPIPE, SIG_IGN);

	for (;;) {

		socklen_t sin_size = sizeof(struct sockaddr_in);
		
		remote_address.sin_family = AF_INET;
		remote_address.sin_port = htons(0);
		remote_address.sin_addr.s_addr = 0;
		memset( &(remote_address.sin_zero), 0, 8);
		
		memset(buf, 0, BUF_SIZE);
		r = recvfrom(s, buf, BUF_SIZE, MSG_PEEK, (struct sockaddr*)&remote_address, &sin_size);
		if ( -1 == r ) {
			perror("recvfrom()");
		}
		
		len = 0;
		while ( (buf[len] != '@') && (len < BUF_SIZE-1)) len++;
		
		memset(buf, 0, BUF_SIZE);
		r = recvfrom(s, buf, len + 1, 0, (struct sockaddr*)&remote_address, &sin_size);
		if ( -1 == r ) {
			perror("recvfrom()");
		}

		sleep(3); /* we are working hard, yeaaah ... */

		memset(buf, 0, BUF_SIZE);
		sprintf(buf, "%d\n", len);
		r = sendto(s, buf, strlen(buf), 0, (struct sockaddr*)&remote_address, sizeof(struct sockaddr_in));
		if ( -1 == r ) {
			perror("sendto()\n");
		};
	};
}

int procnum = DEF_PROC_NUM;
int udp_sock;
struct sockaddr_in local_address;

int main(int argc, char *argv[])
{
	int r, p, pid;
	int yes = 1;

	if (argc > 2) {
		printf("usage: %s [number of server processes]\n", argv[0]);
		exit(1);
	};

	if (argc == 1) {
		printf("number of server process is not specified, defaults to %d\n", procnum);
	} else {
		r = sscanf(argv[1], "%d", &procnum);
		if ( r != 1) {
			printf("number of server process is bad, defaults to %d\n", procnum);
		};
		if (procnum > PROC_NUM_LIMIT) {
			procnum = PROC_NUM_LIMIT;
			printf("number of server process is too large, using max (%d)\n", procnum);
		}
	}

	/* create udp socket */
	udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if ( udp_sock == -1 ) {
		perror("socket()");
		exit(1);
	};
	
	r = setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
	if ( -1 == r ) {
		perror("setsockopt()");
		exit(1);
	};
	
	local_address.sin_family = AF_INET;
	local_address.sin_port = htons(UDP_PORT);
	local_address.sin_addr.s_addr = INADDR_ANY;
	memset( &(local_address.sin_zero), 0, 8);
	
	r = bind(udp_sock, (struct sockaddr*)&local_address, sizeof(struct sockaddr));
	if ( -1 == r ) {
		perror("bind()");
		exit(1);
	}

	/* prefork */
	for (p = 0; p < procnum; p++) {

		pid = fork();
		switch (pid) {

			case -1:
				;;;
				perror("fork()");
				;;;
			break;

			/* child process */
			case 0:
				;;;
				server(udp_sock);
				;;;
			break;

			/* parent */
			default:
				;;;
				printf("child process (pid = %d) created\n", pid);
				;;;
		};
	};

	exit(0);
}