/*
 * iserver2.c
 *   クライアントからの接続要求を受け付けるサーバープログラム。
 *   大文字への変換はfork()で生成された子プロセスが行う。
 *   STREAM型のソケットを使う。
 */

#include < stdio.h >
#include < string.h >
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h >  /* #include < sys/un.h >の代わり */

#define PORT 8765

main()
{
    int    i;
    int    fd1, fd2;
    struct sockaddr_in    saddr;
    struct sockaddr_in    caddr;
    int    len;
    int    ret;
    char   buf[1024];
    int    pid;
    int    msglen;

    /*
     * ソケットを作る。このソケットはUNIXドメインで、ストリーム型ソケット。
     */
    if ((fd1 = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	perror("socket");
	exit(1);
    }

    /* 
     * saddrの中身を0にしておかないと、bind()でエラーが起こることがある
     */
    bzero((char *)&saddr, sizeof(saddr));

    /*
     * ソケットの名前を入れておく
     */
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons(PORT);

    /*
     * ソケットにアドレスをバインドする。
     */
    if (bind(fd1, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){
	perror("bind");
	exit(1);
    }
    
    /*
     * listenをソケットに対して発行する
     */
    if (listen(fd1, 1) < 0) {
	perror("listen");
	exit(1);
    }

    len = sizeof(caddr);
    /*
     * accept()により、クライアントからの接続要求を受け付ける。
     * 成功すると、クライアントと接続されたソケットのディスクリプタが
     * fd2に返される。このfd2を通して通信が可能となる。
     */
    for(;;){
	if ((fd2 = accept(fd1, (struct sockaddr *)&caddr, &len)) < 0) {
	    perror("accept");
	    exit(1);
	}
	/* 
	 *  fork()を使って作業専用のプロセスを作る
	 *  親プロセスは、次の接続要求を待つためにループし、
         *  新しく生成された子プロセスは大文字に変換する作業だけを行う
	 */  
	if ((pid = fork()) < 0) {
	    perror("fork");
	    exit(1);
	} else if (pid == 0) {
	    /* fork()で新しく生成されたプロセスはこちら */
	    close(fd1);
	    ret = read(fd2, buf, 1024);
	    while (strcmp(buf, "quit\n") != 0) {
		/* bufの中の小文字を大文字に変換する */
		for (i=0; i < ret; i++) {
		    if (isalpha(buf[i]))
			buf[i] = toupper(buf[i]);
		}   
		/* 変換したデータをクライアントに送り返す */
		write(fd2, buf, 1024);
		ret = read(fd2, buf, 1024);
	    }
	    exit(0);
	}
	/* 親プロセスはfd2を閉じて接続要求待ちのループに戻る */
	close(fd2);
    }
}