#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>

#include <unistd.h>

int main ()
{
    char buf[13030];
    int len, fd, rc, ipc[2], rc2;

    pid_t ppid = getpid();

    printf("start %d\n", getpid());

    if (socketpair(PF_UNIX, SOCK_DGRAM, 0, ipc) < 0) {
        fprintf(stderr, "socketpair failed\n");
        return 1;
    }

    rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        return 1;
    }

    if (rc == 0) {
        int fdsrv, fdsrv2;
        struct flock flk;

        char control[sizeof(struct cmsghdr)+10];
        struct msghdr msg;
        struct cmsghdr *cmsg;
        struct iovec iov;
        int *fdmsg;

        setsid();
        printf("server start %d\n", getpid());

        fdsrv = open("PDOXUSRS.NET", O_RDWR);
        if (fd < 0) {
            fprintf(stderr, "open failed\n");
            return 1;
        }
        printf("server open for %d\n", fdsrv);

        fdsrv2 = open("PDOXUSRS.NET", O_RDWR);
        if (fd < 0) {
            fprintf(stderr, "second open failed\n");
            return 1;
        }
        printf("server second open for %d\n", fdsrv2);

        flk.l_type = F_WRLCK;
        flk.l_whence = SEEK_SET;
        flk.l_start = 0;
        flk.l_len = 1;
        //flk.l_pid = ppid;

        if (fcntl(fdsrv, F_SETLK, &flk) < 0)
            fprintf(stderr, "fcntl set lock on %d failed (%s)\n", fdsrv, strerror(errno));

        memset(&flk, 0, sizeof(flk));
        flk.l_type = F_WRLCK;
        flk.l_whence = SEEK_SET;
        flk.l_start = 1;
        flk.l_len = 2;
        //flk.l_pid = ppid;

        if (fcntl(fdsrv2, F_SETLK, &flk) < 0)
            fprintf(stderr, "fcntl set lock on %d failed (%s)\n", fdsrv2, strerror(errno));

        if (close(fdsrv2) < 0)
            fprintf(stderr, "close failed %d (%s)\n", fdsrv2, strerror(errno));
        else
            printf("server close %d\n", fdsrv2);

        /* Response data */
        iov.iov_base = "OK";
        iov.iov_len  = 2;

        /* compose the message */
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = control;
        msg.msg_controllen = sizeof(control);

        /* attach open fdsrv */
        cmsg = CMSG_FIRSTHDR(&msg);
        cmsg->cmsg_level = SOL_SOCKET;
        cmsg->cmsg_type = SCM_RIGHTS;
        cmsg->cmsg_len = CMSG_LEN(sizeof(fdsrv));
        fdmsg = (int *)CMSG_DATA(cmsg);
        fdmsg[0] = fdsrv;

        msg.msg_controllen = cmsg->cmsg_len;

        if (sendmsg(ipc[1], &msg, 0) < 0)
                fprintf(stderr,"sendmsg failed\n");

        printf("server send %d\n", fdsrv);

        memset(&flk, 0, sizeof(flk));
        flk.l_type = F_WRLCK;
        flk.l_whence = SEEK_SET;
        flk.l_start = 0;
        flk.l_len = 1;

        if (fcntl(fd, F_GETLK, &flk) < 0)
            fprintf(stderr, "server fcntl get lock failed (%s)\n", strerror(errno));


        if (flk.l_type != F_UNLCK)
            printf("server file locked by %d\n",flk.l_pid);
        else
            printf("server file not locked\n");

        sleep(5);

        printf("server end\n");

        close(fd);

        return 0;
    }

    rc2 = fork();
    if (rc2 < 0) {
        fprintf(stderr, "second fork failed\n");
        return 1;
    }

    if (rc2 == 0) {
        int fdx, lenx;
        struct flock flkx;

        setsid();
        printf("second server start %d\n", getpid());
        sleep(1);

        fdx = open("PDOXUSRS.NET", O_RDWR);
        if (fdx < 0) {
            fprintf(stderr, "second open failed\n");
            return 1;
        }
        printf("second server open for %d\n", fdx);

        flkx.l_type = F_WRLCK;
        flkx.l_whence = SEEK_SET;
        flkx.l_start = 10;
        flkx.l_len = 2;
        //flk.l_pid = ppid;

        if (fcntl(fdx, F_SETLK, &flkx) < 0)
            fprintf(stderr, "second server fcntl set lock on %d failed (%s)\n", fdx, strerror(errno));
        else
            printf("second server lock for %d\n", fdx);

        sleep(1);

        if ((rc = lseek(fdx, 10, SEEK_SET)) < 0)
            fprintf(stderr, "second server lseek failed on %d (%s)\n", fdx, strerror(errno));
        else
            printf("second server lseek done on %d with %d\n", fdx, rc);

        if ((lenx = read(fdx, buf, 2)) < 0)
            fprintf(stderr, "second server read failed on %d (%s)\n", fdx, strerror(errno));
        else
            printf("second server read done on %d with %d\n", fdx, lenx);


        printf("wait second server for end\n");

        sleep(9);

        printf("second server end\n");

        close(fdx);

        return 0;
    }

    char  control[1024];
    struct msghdr msg;
    struct cmsghdr *cmsg;
    struct iovec iov;
    int result;

    struct flock flk;

    memset(&msg, 0, sizeof(msg));
    iov.iov_base = buf;
    iov.iov_len = 1024;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    if (recvmsg(ipc[0], &msg, 0) < 0) {
        fprintf(stderr, "recvmsg failed (%s)\n", strerror(errno));
        return -1;
    }

    fd = -1;
    cmsg = CMSG_FIRSTHDR(&msg);
    while (cmsg != NULL) {
            if (cmsg->cmsg_level == SOL_SOCKET
             && cmsg->cmsg_type  == SCM_RIGHTS) {
                    fd = *(int *) CMSG_DATA(cmsg);
                    printf ("fd received=%d (len=%d)\n", fd,
                            (cmsg->cmsg_len - CMSG_LEN(0))/sizeof(int));
            }
            cmsg = CMSG_NXTHDR(&msg, cmsg);
    }

    sleep(2);

    flk.l_type = F_WRLCK;
    flk.l_whence = SEEK_SET;
    flk.l_start = 1;
    flk.l_len = 2;

    if (fcntl(fd, F_GETLK, &flk) < 0)
        fprintf(stderr, "fcntl get lock on %d failed (%s)\n", fd, strerror(errno));

    if (flk.l_type != F_UNLCK)
        printf("file %d locked by %d\n", fd, flk.l_pid);
    else
        printf("file %d not locked\n", fd);

    if ((len = read(fd, buf, 1)) < 0)
        fprintf(stderr, "first read failed on %d (%s)\n", fd, strerror(errno));
    else
        printf("first read done on %d with %d\n", fd, len);

    if ((rc = lseek(fd, 1, SEEK_SET)) < 0)
        fprintf(stderr, "second lseek failed on %d (%s)\n", fd, strerror(errno));
    else
        printf("second lseek done on %d with %d\n", fd, rc);

    if ((len = read(fd, buf, 2)) < 0)
        fprintf(stderr, "second read failed on %d (%s)\n", fd, strerror(errno));
    else
        printf("second read done on %d with %d\n", fd, len);

    if ((rc = lseek(fd, 10, SEEK_SET)) < 0)
        fprintf(stderr, "third lseek failed on %d (%s)\n", fd, strerror(errno));
    else
        printf("third lseek done on %d with %d\n", fd, rc);

    if ((len = read(fd, buf, 2)) < 0)
        fprintf(stderr, "third read failed on %d (%s)\n", fd, strerror(errno));
    else {
        buf[len] = '\0';
        printf("third read done on %d with %d\ndata:%s\n", fd, len, buf);
    }

    printf("done\n");
}
