/**
 * FSAL_proxy_setclientid_force:
 * Client ID negociation 
 *
 * \param p_context (input):
 *        Authentication context for the operation (user,...).
 *
 * \return Major error codes :
 *        - ERR_FSAL_NO_ERROR     (no error)
 *        - ERR_FSAL_FAULT        (a NULL pointer was passed as mandatory argument)
 *        - Other error codes can be returned :
 *          ERR_FSAL_ACCESS, ERR_FSAL_IO, ...
 */
static fsal_status_t FSAL_proxy_setclientid_force(proxyfsal_op_context_t * p_context)
{
  int rc;
  COMPOUND4args argnfs4;
  COMPOUND4res resnfs4;

#define FSAL_CLIENTID_NB_OP_ALLOC 1
  nfs_argop4 argoparray[FSAL_CLIENTID_NB_OP_ALLOC];
  nfs_resop4 resoparray[FSAL_CLIENTID_NB_OP_ALLOC];

  nfs_client_id4 nfsclientid;
  cb_client4 cbproxy;
  char clientid_name[MAXNAMLEN+1];
  char cbaddr[MAXNAMLEN+1];
  char cbnetid[MAXNAMLEN+1];
  struct timeval timeout = TIMEOUTRPC;
  int fd;
  struct sockaddr_in sin;
  socklen_t l = sizeof(sin);

  LogEvent( COMPONENT_FSAL, "Negotiating a new ClientId with the remote server" ) ;

  /* sanity checks.
   */
  if(!p_context)
    ReturnCode(ERR_FSAL_FAULT, 0);
  if(!CLNT_CONTROL(p_context->rpc_client, CLGET_FD, &fd))
    ReturnCode(ERR_FSAL_FAULT, EBADF);

  if(getsockname(fd, &sin, &l))
    ReturnCode(ERR_FSAL_FAULT, errno);

  /* Client id negociation is to be done only one time for the whole FSAL */
  P(fsal_clientid_mutex_renew);

  /* Setup results structures */
  argnfs4.argarray.argarray_val = argoparray;
  resnfs4.resarray.resarray_val = resoparray;
  argnfs4.minorversion = 0;
  argnfs4.tag.utf8string_val = NULL;
  argnfs4.tag.utf8string_len = 0;
  argnfs4.argarray.argarray_len = 0;

  snprintf(clientid_name, MAXNAMLEN, "%s(%d) - GANESHA NFSv4 Proxy",
           inet_ntop(AF_INET, &sin.sin_addr, cbaddr, sizeof(cbaddr)),
           getpid());

  nfsclientid.id.id_len = strlen(clientid_name);
  nfsclientid.id.id_val = clientid_name;
  snprintf(nfsclientid.verifier, NFS4_VERIFIER_SIZE, "%x", (int)ServerBootTime);

  cbproxy.cb_program = 0;
  strncpy(cbnetid, "tcp", MAXNAMLEN);
  strncpy(cbaddr, "127.0.0.1", MAXNAMLEN);
  cbproxy.cb_location.r_netid = cbnetid;
  cbproxy.cb_location.r_addr = cbaddr;

  COMPOUNDV4_ARG_ADD_OP_SETCLIENTID(argnfs4, nfsclientid, cbproxy);

  TakeTokenFSCall();

  p_context->credential.user = 0;
  p_context->credential.group = 0;
  p_context->credential.nbgroups = 0;

  /* Call the NFSv4 function */
  rc = COMPOUNDV4_EXECUTE_SIMPLE(p_context, argnfs4, resnfs4);
  if(rc != RPC_SUCCESS)
    {
      ReleaseTokenFSCall();

      V(fsal_clientid_mutex_renew);

      ReturnCode(ERR_FSAL_IO, rc);
    }

  ReleaseTokenFSCall();

  if(resnfs4.status != NFS4_OK)
    {
      V(fsal_clientid_mutex_renew);
      return fsal_internal_proxy_error_convert(resnfs4.status,
                                               INDEX_FSAL_InitClientContext);
    }

  /* Step 2: Confirm the client id */
  argnfs4.minorversion = 0;
  argnfs4.tag.utf8string_val = NULL;
  argnfs4.tag.utf8string_len = 0;
  argnfs4.argarray.argarray_len = 0;

  argnfs4.argarray.argarray_val[0].argop = NFS4_OP_SETCLIENTID_CONFIRM;
  argnfs4.argarray.argarray_val[0].nfs_argop4_u.opsetclientid_confirm.clientid =
      resnfs4.resarray.resarray_val[0].nfs_resop4_u.opsetclientid.SETCLIENTID4res_u.
      resok4.clientid;
  memcpy((char *)argnfs4.argarray.argarray_val[0].nfs_argop4_u.opsetclientid_confirm.
         setclientid_confirm,
         (char *)resnfs4.resarray.resarray_val[0].nfs_resop4_u.opsetclientid.
         SETCLIENTID4res_u.resok4.setclientid_confirm, NFS4_VERIFIER_SIZE);
  argnfs4.argarray.argarray_len = 1;

  /* Call the NFSv4 function */
  TakeTokenFSCall();
  rc = COMPOUNDV4_EXECUTE_SIMPLE(p_context, argnfs4, resnfs4);
  if(rc != RPC_SUCCESS)
    {
      ReleaseTokenFSCall();

      V(fsal_clientid_mutex_renew);

      ReturnCode(ERR_FSAL_IO, rc);
    }

  ReleaseTokenFSCall();

  if(resnfs4.status != NFS4_OK)
    {
      V(fsal_clientid_mutex_renew);
      return fsal_internal_proxy_error_convert(resnfs4.status,
                                               INDEX_FSAL_InitClientContext);
    }

  /* Keep the confirmed client id */
  fsal_clientid =
      argnfs4.argarray.argarray_val[0].nfs_argop4_u.opsetclientid_confirm.clientid;
  clientid_renewed = time( NULL ) ;

  V(fsal_clientid_mutex_renew);

  p_context->clientid = fsal_clientid;
  p_context->last_lease_renewal = 0;    /* Needs to be renewed */

  ReturnCode(ERR_FSAL_NO_ERROR, 0);
}                               /* FSAL_proxy_setclientid_force */
/**
 * FSAL_proxy_setclientid_force:
 * Client ID negociation
 *
 * \param p_context (input):
 *        Authentication context for the operation (user,...).
 *
 * \return Major error codes :
 *        - ERR_FSAL_NO_ERROR     (no error)
 *        - ERR_FSAL_FAULT        (a NULL pointer was passed as mandatory argument)
 *        - Other error codes can be returned :
 *          ERR_FSAL_ACCESS, ERR_FSAL_IO, ...
 */
fsal_status_t FSAL_proxy_setclientid_force(proxyfsal_op_context_t * p_context)
{
    int rc;
    fsal_status_t fsal_status;
    COMPOUND4args argnfs4;
    COMPOUND4res resnfs4;

#define FSAL_CLIENTID_NB_OP_ALLOC 1
    nfs_argop4 argoparray[FSAL_CLIENTID_NB_OP_ALLOC];
    nfs_resop4 resoparray[FSAL_CLIENTID_NB_OP_ALLOC];

    nfs_client_id4 nfsclientid;
    cb_client4 cbproxy;
    char clientid_name[MAXNAMLEN];
    char cbaddr[MAXNAMLEN];
    char cbnetid[MAXNAMLEN];
    clientid4 resultclientid;
    struct timeval timeout = TIMEOUTRPC;

    LogEvent( COMPONENT_FSAL, "Negociating a new ClientId with the remote server" ) ;

    /* sanity checks.
     */
    if(!p_context)
        Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_InitClientContext);

    /* Client id negociation is to be done only one time for the whole FSAL */
    P(fsal_clientid_mutex_renew);

    /* Setup results structures */
    argnfs4.argarray.argarray_val = argoparray;
    resnfs4.resarray.resarray_val = resoparray;
    argnfs4.minorversion = 0;
    argnfs4.tag.utf8string_val = NULL;
    argnfs4.tag.utf8string_len = 0;
    argnfs4.argarray.argarray_len = 0;

    snprintf(clientid_name, MAXNAMLEN, "GANESHA NFSv4 Proxy Pid=%u", getpid());
    nfsclientid.id.id_len = strlen(clientid_name);
    nfsclientid.id.id_val = clientid_name;
    snprintf(nfsclientid.verifier, NFS4_VERIFIER_SIZE, "%x", (int)ServerBootTime);

    cbproxy.cb_program = 0;
    strncpy(cbnetid, "tcp", MAXNAMLEN);
    strncpy(cbaddr, "127.0.0.1", MAXNAMLEN);
#ifdef _USE_NFS4_1
    cbproxy.cb_location.na_r_netid = cbnetid;
    cbproxy.cb_location.na_r_addr = cbaddr;
#else
    cbproxy.cb_location.r_netid = cbnetid;
    cbproxy.cb_location.r_addr = cbaddr;
#endif

    COMPOUNDV4_ARG_ADD_OP_SETCLIENTID(argnfs4, nfsclientid, cbproxy);

    TakeTokenFSCall();

    p_context->credential.user = 0;
    p_context->credential.group = 0;
    p_context->credential.nbgroups = 0;

    /* Call the NFSv4 function */
    rc = COMPOUNDV4_EXECUTE_SIMPLE(p_context, argnfs4, resnfs4);
    if(rc != RPC_SUCCESS)
    {
        ReleaseTokenFSCall();

        V(fsal_clientid_mutex_renew);

        Return(ERR_FSAL_IO, rc, INDEX_FSAL_unlink);
    }

    ReleaseTokenFSCall();

    if(resnfs4.status != NFS4_OK)
    {
        V(fsal_clientid_mutex_renew);
        return fsal_internal_proxy_error_convert(resnfs4.status,
                INDEX_FSAL_InitClientContext);
    }

    resultclientid =
        resnfs4.resarray.resarray_val[0].nfs_resop4_u.opsetclientid.SETCLIENTID4res_u.
        resok4.clientid;

    /* Step 2: Confirm the client id */
    argnfs4.minorversion = 0;
    argnfs4.tag.utf8string_val = NULL;
    argnfs4.tag.utf8string_len = 0;
    argnfs4.argarray.argarray_len = 0;

    argnfs4.argarray.argarray_val[0].argop = NFS4_OP_SETCLIENTID_CONFIRM;
    argnfs4.argarray.argarray_val[0].nfs_argop4_u.opsetclientid_confirm.clientid =
        resnfs4.resarray.resarray_val[0].nfs_resop4_u.opsetclientid.SETCLIENTID4res_u.
        resok4.clientid;
    memcpy((char *)argnfs4.argarray.argarray_val[0].nfs_argop4_u.opsetclientid_confirm.
           setclientid_confirm,
           (char *)resnfs4.resarray.resarray_val[0].nfs_resop4_u.opsetclientid.
           SETCLIENTID4res_u.resok4.setclientid_confirm, NFS4_VERIFIER_SIZE);
    argnfs4.argarray.argarray_len = 1;

    /* Call the NFSv4 function */
    rc = COMPOUNDV4_EXECUTE_SIMPLE(p_context, argnfs4, resnfs4);
    if(rc != RPC_SUCCESS)
    {
        ReleaseTokenFSCall();

        V(fsal_clientid_mutex_renew);

        Return(ERR_FSAL_IO, rc, INDEX_FSAL_unlink);
    }

    ReleaseTokenFSCall();

    if(resnfs4.status != NFS4_OK)
        return fsal_internal_proxy_error_convert(resnfs4.status,
                INDEX_FSAL_InitClientContext);

    /* Keep the confirmed client id */
    fsal_clientid =
        argnfs4.argarray.argarray_val[0].nfs_argop4_u.opsetclientid_confirm.clientid;
    clientid_renewed = time( NULL ) ;

    V(fsal_clientid_mutex_renew);

    p_context->clientid = fsal_clientid;
    p_context->last_lease_renewal = 0;    /* Needs to be renewed */

    fsal_status.major = ERR_FSAL_NO_ERROR;
    fsal_status.minor = 0;

    return fsal_status;
}                               /* FSAL_proxy_setclientid_force */