#include <stdio.h>
#include <sys/socket.h> // AF_INET and SOCK_STREAM
#include <arpa/inet.h>  // storage size of serv_addr
#include <unistd.h>     // getpid, getppid, fork
#include <string.h>     // bzero
#include <stdlib.h>
#include <errno.h>
#include <sys/un.h>

#define SOCKETNAME "MySocket"

static int run_server(struct sockaddr_un *sap) {
    int fd_skt, fd_client, fd_hwm = 0, fd;
    char buf[100];
    fd_set set, read_set;
    ssize_t nread;
    printf ("Server pid is %d\n", getpid());
    if ((fd_skt = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n"); return -1; }    
    if (bind(fd_skt, (struct sockaddr *)sap, sizeof(*sap)) < 0) {
        perror("Bind"); exit(EXIT_FAILURE); } 
    if (listen(fd_skt, SOMAXCONN)<0) {
        perror("Listen"); exit(EXIT_FAILURE); }
    if (fd_skt > fd_hwm) fd_hwm = fd_skt;
    FD_ZERO(&set);
    FD_SET(fd_skt, &set);
    while (1) {
        read_set = set;
        if (select(fd_hwm+1, &read_set, NULL, NULL, NULL)==-1) {
            perror("Select"); exit(EXIT_FAILURE); }
        for (fd = 0; fd <= fd_hwm; fd++)
            if (FD_ISSET(fd, &read_set)) {
                if (fd == fd_skt) {
                    if ((fd_client = accept(fd_skt, NULL, 0))<0) {
                        perror("Select"); exit(EXIT_FAILURE); }
                    FD_SET(fd_client, &set);
                    if (fd_client > fd_hwm) fd_hwm = fd_client; }
                else {
                    if ((nread = read(fd, buf, sizeof(buf)))<0) {
                         perror("Read"); exit(EXIT_FAILURE); }
                    if (nread == 0) {
                        FD_CLR(fd, &set);
                        if (fd == fd_hwm) fd_hwm--;
                        close(fd); }
                    else {
                        printf("Server got \"%s\"\n", buf);
                        if (write(fd, "Goodbye!", 9)==-1) {
                            perror("Write"); exit(EXIT_FAILURE); }}}}}
    close(fd_skt);
    close(fd_client);
    printf ("Server terminates\n");
    return 1; }
    

static int run_client(struct sockaddr_un *sap) {
    if (fork() == 0) {
        int fd_skt;
        char buf[100];
        if ((fd_skt = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
             printf("\n Socket creation error \n"); return -1; }            
        while (connect(fd_skt, (struct sockaddr *)sap, sizeof(*sap)) == -1) {
            if (errno == ENOENT) {
                sleep(1);
                continue; }
            else {
             perror("Message from connect [client]");
             exit(-2); }}
        snprintf (buf, sizeof(buf), "Hello from %ld!", (long)getpid());
        if (write (fd_skt, buf, strlen(buf)+1) < 0) {
            perror("Write"); exit(EXIT_FAILURE); }
        if ((read(fd_skt, buf, sizeof(buf))) < 0) {
            perror("Read"); exit(EXIT_FAILURE); }
        printf("Client %d got %s\n", getpid(), buf);
        close(fd_skt);
        printf ("Client %d terminates\n", getpid());
        exit(EXIT_SUCCESS); }
      return 1; }

int main(void) {
    struct sockaddr_un sa;
    int nclient;
    (void)unlink(SOCKETNAME);
    strcpy(sa.sun_path, SOCKETNAME);
    sa.sun_family = AF_UNIX;
    for (nclient = 1; nclient <= 4; nclient++)
         run_client(&sa);
    run_server(&sa);
    exit(EXIT_SUCCESS); }
    
    

