/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
 * Copyright (C) 2021 Red Hat, Inc.
 * All rights reserved.
 *
 * License: GPL (version 3 or any later version).
 * See LICENSE for details.
 * END COPYRIGHT BLOCK **/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


/*
    FILE :        opCheck.c
    AUTHOR :      Jean-Luc SCHWING
    VERSION :     1.0
    DATE :        04 May 1999
    DESCRIPTION :
            This file contains the functions used to manage and
            check the operations performed by the tool.
            These functions manages the operation list
            "mctx.opListTail", match an entry retrieved from the
            server to the attributes memorized for one operation,
            etc...
    LOCAL :        None.
*/

#include <pthread.h>      /* Posix threads */
#include <errno.h>        /* errno, etc... */
#include <stdlib.h>       /* exit(), etc... */
#include <unistd.h>       /* sleep(), etc... */
#include <stdio.h>        /* printf(), etc... */
#include <signal.h>       /* sigset(), etc... */
#include <string.h>       /* strerror(), etc... */
#include <sys/resource.h> /* setrlimit(), etc... */
#include <lber.h>         /* ldap C-API BER decl. */
#include <ldap.h>         /* ldap C-API decl. */
#include <sys/poll.h>     /* djani : while porting */
#include <sys/socket.h>   /* djani : while porting */
#include <sys/types.h>    /* djani : while porting */
#include <netdb.h>        /* djani : while porting */
#include <netinet/in.h>   /* djani : while porting */
#ifdef LDAP_H_FROM_QA_WKA
#include <proto-ldap.h> /* ldap C-API prototypes */
#endif
#include "port.h" /* Portability definitions */ /*JLS 29-11-00*/
#include "ldclt.h"                              /* This tool's include file */
#include "remote.h"                             /* Definitions common with the worker */

enum
{
    SINGLE = 0,
    FIRST,
    MIDDLE,
    LAST
};


/* ****************************************************************************
    FUNCTION :    opDecOper
    PURPOSE :    This function decodes an LDAP operation and return a
            printable string.
    INPUT :        op    = operation to decode
    OUTPUT :    None.
    RETURN :    The decoded string.
    DESCRIPTION :
 *****************************************************************************/
char *
opDecOper(
    int op)
{
    switch (op) {
    case LDAP_REQ_MODIFY:
        return ("modify");
        break;
    case LDAP_REQ_ADD:
        return ("add");
        break;
    case LDAP_REQ_DELETE:
        return ("delete");
        break;
    case LDAP_REQ_MODRDN:
        return ("modrdn");
        break;
    default:
        return ("??unknown??");
        break;
    }
}

/* ****************************************************************************
    FUNCTION :    LDAPMod2attributes
    PURPOSE :    Convert a LDAPMod-like array of attributes to the
            internal attributes array.
    INPUT :        mods    = LDAPMod array. If NULL, attribs[] is
                  initiated as an empty array.
    OUTPUT :    attribs    = struct attribute array. This array is of
                  MAX_ATTRIBS length.
    RETURN :    -1 if error, 0 else.
    DESCRIPTION :
 *****************************************************************************/
int
LDAPMod2attributes(
    LDAPMod **mods,
    attribute *attribs)
{
    int i; /* For the loop */

    /*
   * Maybe there is no mods ?? This occurs for rename operation, for example.
   */
    if (mods == NULL) {
        attribs[0].type = NULL;
        return (0);
    }

    /*
   * Process each entry
   */
    for (i = 0; i < MAX_ATTRIBS && mods[i] != NULL; i++) {
        attribs[i].type = strdup(mods[i]->mod_type);
        if (attribs[i].type == NULL) /*JLS 06-03-00*/
        {                            /*JLS 06-03-00*/
            printf("Error: cannot strdup(attribs[%d].type), error=%d (%s)\n",
                   i, errno, strerror(errno)); /*JLS 06-03-00*/
            return (-1);                       /*JLS 06-03-00*/
        }                                      /*JLS 06-03-00*/

        /*
     * Well, if it is a binary value, it is most likely an image
     * that is read by mmap and always available. Thus there is no reason
     * to copy it, just modify the pointers.
     */
        if (mods[i]->mod_op & LDAP_MOD_BVALUES) {
            attribs[i].dontFree = 1;
            attribs[i].length = mods[i]->mod_bvalues[0]->bv_len;
            attribs[i].value = mods[i]->mod_bvalues[0]->bv_val;
        } else {
            attribs[i].dontFree = 0;
            attribs[i].length = strlen(mods[i]->mod_values[0]);
            attribs[i].value = strdup(mods[i]->mod_values[0]);
            if (attribs[i].value == NULL) /*JLS 06-03-00*/
            {                             /*JLS 06-03-00*/
                printf("Error: cannot strdup(attribs[%d].value), error=%d (%s)\n",
                       i, errno, strerror(errno)); /*JLS 06-03-00*/
                return (-1);                       /*JLS 06-03-00*/
            }                                      /*JLS 06-03-00*/
        }
    }

    /*
   * Don't forget to mark the end !
   */
    if (i < MAX_ATTRIBS)
        attribs[i].type = NULL;
    return (0);
}

/* ****************************************************************************
    FUNCTION :    freeAttributesArray
    PURPOSE :    This function is targeted to free an array of
            struct attribute. It does not free the array itself,
            but only the types and values memorized in it.
    INPUT :        attribs    = array to free.
    OUTPUT :    None.
    RETURN :    -1 if error, 0 else.
    DESCRIPTION :
 *****************************************************************************/
int
freeAttributesArray(
    attribute *attribs)
{
    int i; /* For the loop */

    for (i = 0; i < MAX_ATTRIBS && attribs[i].type != NULL; i++) {
        free(attribs[i].type);
        if (!(attribs[i].dontFree))
            free(attribs[i].value);
    }
    return (0);
}

/* ****************************************************************************
    FUNCTION :    opAdd
    PURPOSE :    Add a new operation to the list.
    INPUT :        tttctx      = thread context
            type      = operation type
            dn      = target's DN
            attribs      = operation attributes
            newRdn      = new rdn    (valid for rename only)
            newParent = new parent (valid for rename only)
    OUTPUT :    None.
    RETURN :    -1 if error, 0 else.
    DESCRIPTION :    Note that the attributes given in argument are
            directly memorized (i.e. no copy), hence they should
            *not* be freed by the calling function.
 *****************************************************************************/
int
opAdd(
    thread_context *tttctx,
    int type,
    char *dn,
    LDAPMod **attribs,
    char *newRdn,
    char *newParent)
{
    int ret;       /* Return value */
    oper *newOper; /* New operation to memorize */

    if (mctx.mode & VERY_VERBOSE)
        printf("T%03d: opAdd (%s, %s)\n", tttctx->thrdNum, opDecOper(type), dn);


    /*
   * Go to protected section. This will enforce the correct sequencing
   * of the operations performed because the whole function is lock
   * for the threads.
   * Note: Maybe reduce the size of this section ? To be checked.
   */
    if ((ret = pthread_mutex_lock(&(mctx.opListTail_mutex))) != 0) {
        fprintf(stderr,
                "T%03d: cannot pthread_mutex_lock(opListTail), error=%d (%s)\n",
                tttctx->thrdNum, ret, strerror(ret));
        fflush(stderr);
        return (-1);
    }

    /*
   * Create the new cell
   */
    newOper = (oper *)malloc(sizeof(oper));
    if (newOper == NULL)                                         /*JLS 06-03-00*/
    {                                                            /*JLS 06-03-00*/
        printf("T%03d: cannot malloc(newOper), error=%d (%s)\n", /*JLS 06-03-00*/
               tttctx->thrdNum, errno, strerror(errno));         /*JLS 06-03-00*/
        return (-1);                                             /*JLS 06-03-00*/
    }                                                            /*JLS 06-03-00*/
    newOper->next = NULL;
    newOper->type = type;
    newOper->skipped = mctx.workersNb;
    newOper->dn = strdup(dn);
    if (newOper->dn == NULL)                                         /*JLS 06-03-00*/
    {                                                                /*JLS 06-03-00*/
        printf("T%03d: cannot strdup(newOper->dn), error=%d (%s)\n", /*JLS 06-03-00*/
               tttctx->thrdNum, errno, strerror(errno));             /*JLS 06-03-00*/
        return (-1);                                                 /*JLS 06-03-00*/
    }                                                                /*JLS 06-03-00*/
    newOper->newRdn = (newRdn == NULL ? NULL : strdup(newRdn));
    if (newOper->newRdn == NULL) /*JLS 06-03-00*/
    {                            /*JLS 06-03-00*/
        printf("T%03d: cannot strdup(newOper->newRdn), error=%d (%s)\n",
               tttctx->thrdNum, errno, strerror(errno)); /*JLS 06-03-00*/
        return (-1);                                     /*JLS 06-03-00*/
    }                                                    /*JLS 06-03-00*/
    newOper->newParent = (newParent == NULL ? NULL : strdup(newParent));
    if (newOper->newParent == NULL) /*JLS 06-03-00*/
    {                               /*JLS 06-03-00*/
        printf("T%03d: cannot strdup(newOper->newParent), error=%d (%s)\n",
               tttctx->thrdNum, errno, strerror(errno)); /*JLS 06-03-00*/
        return (-1);                                     /*JLS 06-03-00*/
    }                                                    /*JLS 06-03-00*/
    if (LDAPMod2attributes(attribs, newOper->attribs) < 0)
        return (-1);

    /*
   * Don't forget to initiate this cell's mutex !
   */
    if ((ret = pthread_mutex_init(&(newOper->skipped_mutex), NULL)) != 0) {
        fprintf(stderr,
                "T%03d: cannot initiate skipped_mutex error=%d (%s)\n",
                tttctx->thrdNum, ret, strerror(ret));
        fflush(stderr);
        return (-1);
    }

    /*
   * Link the cell
   */
    mctx.opListTail->next = newOper;
    mctx.opListTail = newOper;

    /*
   * Release the mutex
   */
    if ((ret = pthread_mutex_unlock(&(mctx.opListTail_mutex))) != 0) {
        fprintf(stderr,
                "T%03d: cannot pthread_mutex_unlock(opListTail), error=%d (%s)\n",
                tttctx->thrdNum, ret, strerror(ret));
        fflush(stderr);
        return (-1);
    }

    return (0);
}

/* ****************************************************************************
    FUNCTION :    opNext
    PURPOSE :    Return the next available operation. May return NULL
            if no operation available.
    INPUT :        ctctx    = thread context
    OUTPUT :    op    = next operation. May be NULL.
    RETURN :    -1 if error, 0 else.
    DESCRIPTION :
 *****************************************************************************/
int
opNext(
    check_context *ctctx,
    oper **op)
{
    int ret;       /* Return value */
    oper *newHead; /* The new head operation */

    /*
   * Maybe there is no new operation ?
   */
    if (ctctx->headListOp->next == NULL) {
        *op = NULL;
        if (mctx.mode & VERY_VERBOSE)
            printf("C%03d: opNext --> NULL\n", ctctx->thrdNum);
        return (0);
    }

    /*
   * Ok, there is one new operation. Let's skip the head and
   * go to the new operation...
   */
    if ((ret = pthread_mutex_lock(&(ctctx->headListOp->skipped_mutex))) != 0) {
        fprintf(stderr,
                "C%03d: cannot pthread_mutex_lock(skipped_mutex), error=%d (%s)\n",
                ctctx->thrdNum, ret, strerror(ret));
        fflush(stderr);
        return (-1);
    }
    newHead = ctctx->headListOp->next;
    ctctx->headListOp->skipped--;

    /*
   * If there is another thread that has not skipped, let's move to the
   * next operation and unlock the counter.
   */
    if (ctctx->headListOp->skipped != 0) {
        if ((ret = pthread_mutex_unlock(&(ctctx->headListOp->skipped_mutex))) != 0) {
            fprintf(stderr,
                    "C%03d: cannot pthread_mutex_unlock(skipped_mutex), error=%d (%s)\n",
                    ctctx->thrdNum, ret, strerror(ret));
            fflush(stderr);
            return (-1);
        }
    } else {
        /*
     * Well, looks like we are the last thread to skip.... Let's free this
     * operation. BTW, there is no reason to unlock/release the mutex because
     * it will be destroyed !
     * Note: may be NULL when LDAP_REQ_DELETE for example.
     */
        if (ctctx->headListOp->attribs != NULL)
            if (freeAttributesArray(ctctx->headListOp->attribs) < 0)
                return (-1);
        if (ctctx->headListOp->dn != NULL)
            free(ctctx->headListOp->dn);
        if (ctctx->headListOp->newRdn != NULL)
            free(ctctx->headListOp->newRdn);
        if (ctctx->headListOp->newParent != NULL)
            free(ctctx->headListOp->newParent);
        free(ctctx->headListOp);
    }

    /*
   * End of function
   */
    *op = ctctx->headListOp = newHead;
    if (mctx.mode & VERY_VERBOSE)
        printf("C%03d: opNext --> (%s, %s)\n",
               ctctx->thrdNum, opDecOper((*op)->type), (*op)->dn);
    return (0);
}

/* ****************************************************************************
    FUNCTION :    opRead
    PURPOSE :    Read the n'th operation from the head.
    INPUT :        ctctx    = thread context
            num    = number of the operation to retrieve
    OUTPUT :    op    = returned operation. May be NULL.
    RETURN :    -1 if error, 0 else.
    DESCRIPTION :
 *****************************************************************************/
int
opRead(
    check_context *ctctx,
    int num,
    oper **op)
{
    *op = ctctx->headListOp;
    while (num != 0) {
        /*
     * Maybe not enough entries in the list ?
     */
        if (*op == NULL)
            return (0);
        *op = (*op)->next;
        num--;
    }

    /*
   * If there, we got it :-)
   */
    return (0);
}

/* ****************************************************************************
    FUNCTION :    thOperAdd
    PURPOSE :    This function copies an operation to the late
            operation list
    INPUT :        head list and operation to copy
    OUTPUT :    None.
    RETURN :    New head
    DESCRIPTION :
 *****************************************************************************/
thoper *
thOperAdd(thoper *head, oper *elem, int f)
{
    thoper *new, *t = head;
    int i;

    new = malloc(sizeof(thoper));
    if (new == NULL)                                        /*JLS 06-03-00*/
    {                                                       /*JLS 06-03-00*/
        printf("Txxx: cannot malloc(new), error=%d (%s)\n", /*JLS 06-03-00*/
               errno, strerror(errno));                     /*JLS 06-03-00*/
        ldcltExit(1);                                       /*JLS 18-08-00*/
    }                                                       /*JLS 06-03-00*/
    new->next = NULL;
    new->first = f;
    new->type = elem->type;
    new->dn = strdup(elem->dn);
    if (elem->newRdn != NULL)
        new->newRdn = strdup(elem->newRdn);
    if (elem->newParent != NULL)
        new->newParent = strdup(elem->newParent);
    for (i = 0; i < MAX_ATTRIBS && elem->attribs[i].type; i++) {
        new->attribs[i].type = strdup(elem->attribs[i].type);
        if (new->attribs[i].type == NULL) /*JLS 06-03-00*/
        {                                 /*JLS 06-03-00*/
            printf("Txxx: cannot strdup(new->attribs[%d].type), error=%d (%s)\n",
                   errno, i, strerror(errno)); /*JLS 06-03-00*/
            ldcltExit(1);                      /*JLS 18-08-00*/
        }                                      /*JLS 06-03-00*/
        new->attribs[i].length = elem->attribs[i].length;
        if ((new->attribs[i].dontFree = elem->attribs[i].dontFree))
            new->attribs[i].value = elem->attribs[i].value;
        else {
            new->attribs[i].value = strdup(elem->attribs[i].value);
            if (new->attribs[i].value == NULL) /*JLS 06-03-00*/
            {                                  /*JLS 06-03-00*/
                printf("Txxx: cannot strdup(new->attribs[%d].value), error=%d (%s)\n",
                       errno, i, strerror(errno)); /*JLS 06-03-00*/
                ldcltExit(1);                      /*JLS 18-08-00*/
            }                                      /*JLS 06-03-00*/
        }
    }
    if (i < MAX_ATTRIBS)
        new->attribs[i].type = NULL;
    if (head == NULL)
        return new;
    for (t = head; t->next;)
        t = t->next;
    t->next = new;
    return head;
}

/* ****************************************************************************
    FUNCTION :    thOperFree
    PURPOSE :    This function frees memory for a late operation
    INPUT :        Head of list and operation to delete
    OUTPUT :    None.
    RETURN :    new head
    DESCRIPTION :
 *****************************************************************************/
thoper *
thOperFree(thoper *head, thoper *elem)
{
    thoper *t;

    freeAttributesArray(elem->attribs);
    free(elem->dn);
    if (elem->newRdn != NULL)
        free(elem->newRdn);
    if (elem->newParent != NULL)
        free(elem->newParent);
    if (head != elem) {
        for (t = head; t->next != elem;)
            t = t->next;
        t->next = t->next->next;
    } else
        head = head->next;
    free(elem);
    return head;
}

/* ****************************************************************************
    FUNCTION :    opCheckLoop
    PURPOSE :    This function is the per worker check function
    INPUT :        arg    = this check thread's check_context
    OUTPUT :    None.
    RETURN :    None.
    DESCRIPTION :
 *****************************************************************************/
void *
opCheckLoop(void *arg)
{
    struct check_context *cctx = (struct check_context *)arg;
    struct pollfd pfd;
    repconfirm *recOper;
    oper *myop;
    thoper *t;
    unsigned char recbuf[1500];
    int ret, i, timeout;
    int cnt;                        /* To count loops for timeout purpose */
    int fndlt;                      /* Found late operation */
    int nbRead;                     /* Nb char read() */
    int status; /* Thread status */ /*JLS 17-11-00*/

    recOper = (repconfirm *)recbuf;
    pfd.fd = cctx->sockfd;
    pfd.events = (POLLIN | POLLPRI);
    pfd.revents = 0;
    cctx->status = INITIATED;
    if ((timeout = mctx.timeout) < 30)
        timeout = 30;
    /*
   * First time in here?
   */
    if (cctx->calls == 1)
        cctx->dcOper = NULL;

    while ((ret = poll(&pfd, 1, 500)) >= 0) {
        if (ret) {
            /*
         * Exit if read error on the net
         */
            if ((nbRead = read(pfd.fd, recOper, sizeof(repconfirm))) < 0)
                break;
            if (nbRead != sizeof(repconfirm))
                printf("C%03d(%s): Partial header read %d - expected %d\n", cctx->thrdNum, cctx->workerName, nbRead, sizeof(repconfirm));
            recOper->type = ntohl(recOper->type);
            recOper->res = ntohl(recOper->res);
            recOper->dnSize = ntohl(recOper->dnSize);
            /*
         * Beware of structure alignment
         */
            if ((nbRead = read(pfd.fd, recOper->dn + sizeof(recOper->dn), recOper->dnSize)) < 0)
                break;
            if (nbRead != recOper->dnSize)
                printf("C%03d(%s): Partial dn read %d - expected %d\n", cctx->thrdNum, cctx->workerName, nbRead, recOper->dnSize);
            if (nbRead > (1500 - sizeof(repconfirm)))
                printf("C%03d(%s): Read too much %d - expected %d\n", cctx->thrdNum, cctx->workerName, nbRead, 1500 - sizeof(repconfirm));
            cnt = 0;
            cctx->nbOpRecv++;
            if (mctx.mode & VERY_VERBOSE) {
                printf("C%03d(%s): Rec %s\n", cctx->thrdNum, cctx->workerName, recOper->dn);
                for (myop = cctx->headListOp->next; myop; myop = myop->next)
                    printf("C%03d(%s): IN : %s\n", cctx->thrdNum, cctx->workerName, myop->dn);
                for (t = cctx->dcOper; t; t = t->next)
                    printf("C%03d(%s): LATE : %s\n", cctx->thrdNum, cctx->workerName, t->dn);
            }
            /*
         * Do not tell me there was an error during replica...
         */
            if (recOper->res) {
                printf("C%03d(%s): Replica failed, op:%d(%s), dn=\"%s\" res=%d\n",
                       cctx->thrdNum, cctx->workerName,
                       recOper->type, opDecOper(recOper->type),
                       recOper->dn, recOper->res);
                switch (recOper->res) {
                case 32:
                    cctx->nbRepFail32++;
                    break;
                case 68:
                    cctx->nbRepFail68++;
                    break;
                default:
                    cctx->nbRepFailX++;
                    break;
                }
            }
            /*
         * Is this a late operation?
         */
            fndlt = 0;
            if (cctx->dcOper) {
                for (i = 1, t = cctx->dcOper; t; i++)
                    if ((recOper->type != t->type) || strcmp(recOper->dn, t->dn))
                        t = t->next;
                    else {
                        /*
                     * if this is a single operation: 132456
                     */
                        if (t->first == SINGLE) {
                            /*
                         * error.
                         */
                            printf("C%03d(%s): Late replica:   op:%d(%s), dn=\"%s\"\n",
                                   cctx->thrdNum, cctx->workerName,
                                   t->type, opDecOper(t->type), t->dn);
                            cctx->nbLate++;
                        } else if (t->first == MIDDLE) {
                            /*
                         * Middle of a series : 23546
                         */
                            printf("C%03d(%s): Early (%3d)     op:%d(%s), dn=\"%s\"\n",
                                   cctx->thrdNum, cctx->workerName, i,
                                   t->type, opDecOper(t->type), t->dn);
                            cctx->nbEarly++;

                        } else if (t->next) {
                            /*
                         * else maybe we are in a re-corrected situation.
                         * we should receive the next one, now.
                         */
                            if (t->next->first != LAST)
                                t->next->first = FIRST;
                        }
                        cctx->dcOper = thOperFree(cctx->dcOper, t);
                        fndlt = 1;
                        break;
                    }
            }
            if (!fndlt) {
                /*
             * See if the operation we received is the same as the head
             */
                opRead(cctx, 1, &myop);
                if (myop != NULL &&
                    recOper->type == myop->type &&
                    strcmp(recOper->dn, myop->dn) == 0)
                    opNext(cctx, &myop);
                else {
                    /*
                 * Nope, look for it
                 */
                    for (i = 2; opRead(cctx, i, &myop) == 0; i++)
                        if (myop) {
                            if (recOper->type == myop->type &&
                                strcmp(recOper->dn, myop->dn) == 0) {
                                /*
                             * Skip all between current head and this one
                             */
                                printf("C%03d(%s): Early (%3d)     op:%d(%s), dn=\"%s\"\n", cctx->thrdNum, cctx->workerName, i - 1, recOper->type, opDecOper(recOper->type), recOper->dn);
                                cctx->nbEarly++;
                                opNext(cctx, &myop);
                                /*
                             * mark the first of the series as leader
                             */
                                cctx->dcOper = thOperAdd(cctx->dcOper, myop,
                                                         i == 2 ? SINGLE : FIRST);
                                for (; i > 2; i--) {
                                    opNext(cctx, &myop);
                                    /*
                                 * copy up until one before last
                                 */
                                    if (myop)
                                        thOperAdd(cctx->dcOper, myop,
                                                  i == 3 ? LAST : MIDDLE);
                                }
                                opNext(cctx, &myop);
                                break;
                            }
                        } else
                            break;
                    if (!myop) {
                        printf("C%03d(%s): Not on list     op:%d(%s), dn=\"%s\"\n", cctx->thrdNum, cctx->workerName, recOper->type, opDecOper(recOper->type), recOper->dn);
                        cctx->nbNotOnList++;
                    }
                }
            }
        }
        pfd.events = (POLLIN | POLLPRI);
        pfd.revents = 0;
        /*
     * operations threads still running?
     */
        for (i = 0; i < mctx.nbThreads; i++) {            /*JLS 17-11-00*/
            if (getThreadStatus(&(tctx[i]), &status) < 0) /*JLS 17-11-00*/
                break;                                    /*JLS 17-11-00*/
            if (status != DEAD)                           /*JLS 17-11-00*/
            {
                cnt = 0;
                break;
            }
        } /*JLS 17-11-00*/
        /*
     * twice half a second...
     */
        if (++cnt > timeout * 2)
            break;
    }
    if (mctx.mode & VERY_VERBOSE)
        printf("C%03d(%s): Exiting\n", cctx->thrdNum, cctx->workerName);
    /*
   * Any operation left?
   */
    for (opNext(cctx, &myop); myop; opNext(cctx, &myop)) {
        printf("Operation %d(%s) still on Queue for %s (%s)\n", myop->type, opDecOper(myop->type), cctx->workerName, myop->dn);
        cctx->nbStillOnQ++;
    }
    for (t = cctx->dcOper; t; t = t->next) {
        printf("Lost op %d(%s) on %s (%s)\n", t->type, opDecOper(t->type), cctx->workerName, t->dn);
        cctx->nbLostOp++;
    }
    close(cctx->sockfd);
    cctx->status = DEAD;
    pthread_exit(NULL);
}

/* ****************************************************************************
    FUNCTION :  opCheckMain
    PURPOSE :   This function is the main function of the check operation threads,
    INPUT :     arg = NULL
    OUTPUT :    None.
    RETURN :    None.
    DESCRIPTION :
 *****************************************************************************/
void *
opCheckMain(
    void *arg)
{
    struct sockaddr_in srvsaddr, claddr;
    struct hostent cltaddr;
#ifdef LINUX
    struct hostent *stupidlinux = NULL;
#endif
    struct linger lopt;
    uint32_t ipaddr;
    int newfd, sockfd, ncctx, i, err;
    char buffer[128];
    int retry; /* To retry on EINTR */

    /*
     * Initialization
     */

    srvsaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    srvsaddr.sin_family = AF_INET;
    srvsaddr.sin_port = htons(supplierPort);

    /*
   * Let's go !!!
   */

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Socket");
        ldcltExit(1); /*JLS 18-08-00*/
    }
    i = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(int)) != 0)
        perror("Sockopt");
    if (bind(sockfd, (struct sockaddr *)&srvsaddr, sizeof(struct sockaddr)) != 0) {
        perror("Bind");
        ldcltExit(1); /*JLS 18-08-00*/
    }
    if (listen(sockfd, 1) != 0)
        perror("listen");
    for (ncctx = 0;;) {
        i = sizeof(claddr);
        retry = 1;
        while (retry) {
            if ((newfd = accept(sockfd, (struct sockaddr *)&claddr, &i)) >= 0)
                retry = 0;
            else if (errno != EINTR) {
                perror("Accept");
                ldcltExit(1); /*JLS 18-08-00*/
            }
        }
        /*
     * get client's name
     */
        ipaddr = ntohl(claddr.sin_addr.s_addr);

#ifdef LINUX
        gethostbyaddr_r((char *)&ipaddr, sizeof(ipaddr), AF_INET, &cltaddr,
                        buffer, 128, &stupidlinux, &err);
#else
#if defined(HPUX) && defined(__LP64__)
        gethostbyaddr((char *)&ipaddr, sizeof(ipaddr), AF_INET);
#else
        gethostbyaddr_r((char *)&ipaddr, sizeof(ipaddr), AF_INET, &cltaddr,
                        buffer, 128, &err);
#endif
#endif

        i = 1;
        if (setsockopt(newfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(int)) != 0)
            perror("Nagle");
        /*
     * Linger: when connection ends, send an RST instead of a FIN
     * This way the client will have a fail on the first write instead of
     * the second
     */
        lopt.l_onoff = 1;
        lopt.l_linger = 0;
        if (setsockopt(newfd, SOL_SOCKET, SO_LINGER, (void *)&lopt, sizeof(struct linger)) < 0)
            perror("Linger");
        /*
     * Search for an empty client slot. If a client reconnects, use the
     * same slot
     */
        for (i = 0; i < mctx.workersNb; i++) {
            if (cctx[i].calls == 0) {
                i = ncctx++;
                break;
            }
            if (cctx[i].workerName && cctx[i].status == DEAD)
                if (strcmp(cctx[i].workerName, cltaddr.h_name) == 0)
                    break;
        }
        if (i >= mctx.workersNb) {
            fprintf(stderr, "ldclt: Too many workers %s\n", cltaddr.h_name);
            close(newfd);
            continue;
        }

        cctx[i].sockfd = newfd;
        cctx[i].calls++;
        cctx[i].workerName = strdup(cltaddr.h_name);
        if ((err = pthread_create(&(cctx[i].tid), NULL,
                                  opCheckLoop, (void *)&(cctx[i]))) != 0) {
            fprintf(stderr, "ldclt: %s\n", strerror(err));
            fprintf(stderr, "Error: cannot create thread opCheck for %s\n",
                    cltaddr.h_name);
            fflush(stderr);
        } else
            mctx.workerConn = 1;
    }
    close(sockfd);
}
