/*___INFO__MARK_BEGIN__*/
/*************************************************************************
 * 
 *  The Contents of this file are made available subject to the terms of
 *  the Sun Industry Standards Source License Version 1.2
 * 
 *  Sun Microsystems Inc., March, 2001
 * 
 * 
 *  Sun Industry Standards Source License Version 1.2
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.2 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html
 * 
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 * 
 *   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 * 
 *   Copyright: 2001 by Sun Microsystems, Inc.
 * 
 *   All Rights Reserved.
 * 
 ************************************************************************/
/*___INFO__MARK_END__*/

#include <stdlib.h>
#include <string.h>

#ifdef KERBEROS
#  include "krb_lib.h"
#endif

#include "basis_types.h"

#include "comm/commlib.h"

#include "rmon/sgermon.h"

#include "lck/sge_mtutil.h"

#include "uti/sge_log.h"
#include "uti/sge_tq.h"

#include "sgeobj/sge_answer.h"
#include "sgeobj/sge_multi_MA_L.h"
#include "sgeobj/sge_jsv.h"

#include "sge_gdi2.h"
#include "sge_gdi_packet_pb_cull.h"
#include "sge_security.h"
#include "sge_gdi_packet.h"

#include "msg_common.h"
#include "msg_gdilib.h"

#define CLIENT_WAIT_TIME_S 1

sge_tq_queue_t *Master_Task_Queue = NULL;

/****** gdi/request_internal/sge_gdi_packet_create_multi_answer() ***********
*  NAME
*     sge_gdi_packet_create_multi_answer() -- create multi answer 
*
*  SYNOPSIS
*     static bool 
*     sge_gdi_packet_create_multi_answer(sge_gdi_ctx_class_t* ctx, 
*                                        lList **answer_list, 
*                                        sge_gdi_packet_class_t **packet, 
*                                        lList **malpp) 
*
*  FUNCTION
*     Creates a multi answer element ("malpp") from the given "packet".
*     The lists and answer lists for the (multi) GDI request will be
*     moved from the task structures conteined in the packet, into the
*     multi answer list. After all information has been moved the packet
*     and all subelement will be freed so that the *packet will be NULL
*     when the function returns.
*
*     Threre are no errors expected from this function. So the return 
*     value "false" or a filled "answer_list" will never bee seen
*     after return.
*
*  INPUTS
*     sge_gdi_ctx_class_t* ctx        - context (not used) 
*     lList **answer_list             - answer_list (not used) 
*     sge_gdi_packet_class_t **packet - packet 
*     lList **malpp                   - multi answer
*
*  RESULT
*     static bool - error state
*        true  - always
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_create_multi_answer() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/sge_gdi_packet_execute_external() 
*     gdi/request_internal/sge_gdi_packet_execute_internal() 
*     gdi/request_internal/sge_gdi_packet_wait_for_result_external()
*     gdi/request_internal/sge_gdi_packet_wait_for_result_internal()
*     sge_gdi_packet_is_handled()
*******************************************************************************/
static bool
sge_gdi_packet_create_multi_answer(sge_gdi_ctx_class_t* ctx, lList **answer_list,
                                   sge_gdi_packet_class_t **packet, lList **malpp)
{
   sge_gdi_task_class_t *task = NULL;
   bool ret = true;

   DENTER(TOP_LAYER, "sge_packet_create_multi_answer");

   /* 
    * make multi answer list and move all data contained in packet 
    * into that structure 
    */
   task = (*packet)->first_task;
   while (task != NULL) {
      u_long32 operation = SGE_GDI_GET_OPERATION(task->command);
      u_long32 sub_command = SGE_GDI_GET_SUBCOMMAND(task->command);
      lListElem *map = lAddElemUlong(malpp, MA_id, task->id, MA_Type);

      if (operation == SGE_GDI_GET || operation == SGE_GDI_PERMCHECK ||
          (operation == SGE_GDI_ADD && sub_command == SGE_GDI_RETURN_NEW_VERSION)) {
         lSetList(map, MA_objects, task->data_list);
         task->data_list = NULL;
      }

      lSetList(map, MA_answers, task->answer_list);
      task->answer_list = NULL;

      task = task->next;
   }

   /*
    * It is time to free the element. It is really not needed anymore.
    */
   sge_gdi_packet_free(packet);

   DRETURN(ret);
}


/****** gdi/request_internal/sge_gdi_packet_wait_till_handled() *************
*  NAME
*     sge_gdi_packet_wait_till_handled() -- wait til packet is handled 
*
*  SYNOPSIS
*     void 
*     sge_gdi_packet_wait_till_handled(sge_gdi_packet_class_t *packet) 
*
*  FUNCTION
*     This function blocks the calling thread till another one executes
*     sge_gdi_packet_broadcast_that_handled(). Mutiple threads can use
*     this call to get response if the packet is accessed by someone 
*     else anymore.
*
*     This function is used to synchronize packet producers (listerner,
*     scheduler, jvm thread ...) with packet consumers (worker threads)
*     which all use a packet queue to synchronize the access to
*     packet elements. 
*
*     Packet producers store packets in the packet queue and then
*     they call this function to wait that they can access the packet
*     structure again. 
*
*  INPUTS
*     sge_gdi_packet_class_t *packet - packet element 
*
*  RESULT
*     void - none
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_wait_till_handled() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/Master_Packet_Queue
*     gdi/request_internal/sge_gdi_packet_queue_wait_for_new_packet()
*     gdi/request_internal/sge_gdi_packet_queue_store_notify()
*     gdi/request_internal/sge_gdi_packet_broadcast_that_handled()
*     gdi/request_internal/sge_gdi_packet_is_handled()
*******************************************************************************/
void
sge_gdi_packet_wait_till_handled(sge_gdi_packet_class_t *packet)
{
   DENTER(TOP_LAYER, "sge_gdi_packet_wait_till_handled");

   if (packet != NULL) {
      sge_mutex_lock(GDI_PACKET_MUTEX, SGE_FUNC, __LINE__, &(packet->mutex));

      while (packet->is_handled == false) {
         struct timespec ts; 

         DPRINTF(("waiting for packet to be handling by worker\n"));
         sge_relative_timespec(CLIENT_WAIT_TIME_S, &ts);
         pthread_cond_timedwait(&(packet->cond), &(packet->mutex), &ts);
      }

      sge_mutex_unlock(GDI_PACKET_MUTEX, SGE_FUNC, __LINE__, &(packet->mutex));

      DPRINTF(("got signal that packet is handled\n"));
   }

   DRETURN_VOID;   
}

/****** gdi/request_internal/sge_gdi_packet_is_handled() ********************
*  NAME
*     sge_gdi_packet_is_handled() -- returns if packet was handled by worker
*
*  SYNOPSIS
*     void 
*     sge_gdi_packet_is_handled(sge_gdi_packet_class_t *packet) 
*
*  FUNCTION
*     Returns if the given packet was already handled by a worker thread.
*     "true" means that the packet is completely done so that a call
*     to sge_gdi_packet_wait_till_handled() will return immediately. If 
*     "false" is returned the the packet is not finished so a call to
*     sge_gdi_packet_wait_till_handled() might block when it is called 
*     afterwards.
*
*  INPUTS
*     sge_gdi_packet_class_t *packet - packet element 
*
*  RESULT
*     bool - true    packet was already handled by a worker
*            false   packet is not done. 
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_is_handled() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/Master_Packet_Queue
*     gdi/request_internal/sge_gdi_packet_queue_wait_for_new_packet()
*     gdi/request_internal/sge_gdi_packet_queue_store_notify()
*     gdi/request_internal/sge_gdi_packet_broadcast_that_handled()
*******************************************************************************/
bool
sge_gdi_packet_is_handled(sge_gdi_packet_class_t *packet)
{
   bool ret = true;

   DENTER(TOP_LAYER, "sge_gdi_packet_wait_till_handled");
   if (packet != NULL) {   
      sge_mutex_lock(GDI_PACKET_MUTEX, SGE_FUNC, __LINE__, &(packet->mutex));
      ret = packet->is_handled;
      sge_mutex_unlock(GDI_PACKET_MUTEX, SGE_FUNC, __LINE__, &(packet->mutex));
   }
   DRETURN(ret);
}

/****** gdi/request_internal/sge_gdi_packet_broadcast_that_handled() ********
*  NAME
*     sge_gdi_packet_broadcast_that_handled() -- broadcast to waiting threads 
*
*  SYNOPSIS
*     void 
*     sge_gdi_packet_broadcast_that_handled(sge_gdi_packet_class_t *packet) 
*
*  FUNCTION
*     This functions wakes up all threads waiting in 
*     sge_gdi_packet_wait_till_handled(). 
*
*     This function is used to synchronize packet producers (listerner,
*     scheduler, jvm thread ...) with packet consumers (worker threads)
*     which all use a packet queue to synchronize the access to
*     packet elements. 
*
*     Packet producers store packets in the packet queue and then
*     they call sge_gdi_packet_wait_till_handled(). Packet consumers
*     fetch a packet from the queue. After they have finished using
*     the packet structure they call this function to notify
*     the waiting threads that the packet is not accessed anymore.
*
*  INPUTS
*     sge_gdi_packet_class_t *packet - packet element 
*
*  RESULT
*     void - NONE
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_broadcast_that_handled() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/Master_Packet_Queue
*     gdi/request_internal/sge_gdi_packet_queue_wait_for_new_packet()
*     gdi/request_internal/sge_gdi_packet_queue_store_notify()
*     gdi/request_internal/sge_gdi_packet_wait_till_handled()
*******************************************************************************/
void
sge_gdi_packet_broadcast_that_handled(sge_gdi_packet_class_t *packet)
{
   DENTER(TOP_LAYER, "sge_gdi_packet_broadcast_that_handled");

   sge_mutex_lock(GDI_PACKET_MUTEX, SGE_FUNC, __LINE__, &(packet->mutex));
   packet->is_handled = true; 
   DPRINTF(("broadcast that packet is handled\n"));
   pthread_cond_broadcast(&(packet->cond));
   sge_mutex_unlock(GDI_PACKET_MUTEX, SGE_FUNC, __LINE__, &(packet->mutex));

   DRETURN_VOID;   
}


/****** gdi/request/get_gdi_retries_value() ***************************************
*  NAME
*     get_gdi_retries_value() -- get commlib parameter list value "gdi_retries"
*
*  SYNOPSIS
*     static int get_gdi_retries_value(void) 
*
*  FUNCTION
*     Returns the nr of retries on synchron receive retries when getting a
*     GDI response message.
*
*  INPUTS
*     void - none
*
*  RESULT
*     static int - configured "gdi_retries" value (set by qmaster_params)
*
*  SEE ALSO
*     gdi/request/get_cl_ping_value()
*     gdi/request/get_gdi_retries_value()
*******************************************************************************/
static int get_gdi_retries_value(void) {
   char* gdi_retries = NULL;
   int retries = 0;
   cl_com_get_parameter_list_value("gdi_retries", &gdi_retries);
   if (gdi_retries != NULL) {
      retries = atoi(gdi_retries);
      FREE(gdi_retries);
   }
   return retries;
}

/****** gdi/request/get_cl_ping_value() *******************************************
*  NAME
*     get_cl_ping_value() -- get commlib parameter list value "cl_ping"
*
*  SYNOPSIS
*     static bool get_cl_ping_value(void) 
*
*  FUNCTION
*     Returns the value of the "cl_ping" commlib parameter. The value is true
*     or false and specifies if gdi should send a SIM message to qmaster which
*     should be used for desiction making if qmaster is still working or
*     unreachable.
*
*  INPUTS
*     void - none
*
*  RESULT
*     static bool - true or false (enabled or disabled)
*
*  SEE ALSO
*     gdi/request/get_cl_ping_value()
*     gdi/request/get_gdi_retries_value()
*******************************************************************************/
static bool get_cl_ping_value(void) {
   char* cl_ping = NULL;
   bool do_ping = false;

   cl_com_get_parameter_list_value("cl_ping", &cl_ping);
   if (cl_ping != NULL) {
      if (strcasecmp(cl_ping, "true") == 0) {
         do_ping = true;
      }
      FREE(cl_ping);
   }
   return do_ping;
}



/****** gdi/request_internal/sge_gdi_packet_execute_external() ****************
*  NAME
*     sge_gdi_packet_execute_external() -- execute a GDI packet 
*
*  SYNOPSIS
*     bool 
*     sge_gdi_packet_execute_external(sge_gdi_ctx_class_t* ctx, 
*                                     lList **answer_list, 
*                                     sge_gdi_packet_class_t *packet) 
*
*  FUNCTION
*     This functions sends a GDI "packet" from an external client
*     to the qmaster process. If the packet is handled on master side
*     the response is send back to the client which then will fill
*     "packet" with the received information.
*
*     To send packets from internal clients (threads) the function 
*     sge_gdi_packet_execute_internal() has to be used.
*
*     Please note that in contrast to sge_gdi_packet_execute_internal()
*     this function assures that the GDI request contained in the
*     "packet" is completely executed (either successfull or with errors)
*     after this function returns.
*
*     a GDI multi answer lists structure from the information contained
*     in the packet after this function has been called.
*     
*  INPUTS
*     sge_gdi_ctx_class_t* ctx       - context handle 
*     lList **answer_list            - answer list 
*     sge_gdi_packet_class_t *packet - packet 
*
*  RESULT
*     bool - error state
*        true   - success
*        false  - error (answer_lists will contain details)
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_execute_extern() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/sge_gdi_packet_execute_external() 
*     gdi/request_internal/sge_gdi_packet_execute_internal() 
*     gdi/request_internal/sge_gdi_packet_wait_for_result_external()
*     gdi/request_internal/sge_gdi_packet_wait_for_result_internal()
*******************************************************************************/
bool 
sge_gdi_packet_execute_external(sge_gdi_ctx_class_t* ctx, lList **answer_list, 
                                sge_gdi_packet_class_t *packet) 
{
   bool ret = true;
   sge_pack_buffer pb;
   bool pb_initialized = false;
   sge_pack_buffer rpb;
   sge_gdi_packet_class_t *ret_packet = NULL;
   int commlib_error;
   u_long32 message_id;

   DENTER(TOP_LAYER, "sge_gdi_packet_execute_external");

   /* here the packet gets a unique request id */
   packet->id = gdi_state_get_next_request_id();

#ifdef KERBEROS
   /* request that the Kerberos library forward the TGT */
   if (ret && packet->first_task->target == SGE_JB_LIST && 
       SGE_GDI_GET_OPERATION(packet->first_task->command) == SGE_GDI_ADD ) {
      krb_set_client_flags(krb_get_client_flags() | KRB_FORWARD_TGT);
      krb_set_tgt_id(packet->id);
   }
#endif

    /*
     * Now we will execute the JSV script if we got a job submission request.
     * It is necessary to dechain the job which is verified because the
     * job verification process might destroy the job and create a completely
     * new one with adjusted job attributes.
     */
    if (ret) {
       sge_gdi_task_class_t *task = packet->first_task;

       if (task->target == SGE_JB_LIST &&
           ((SGE_GDI_GET_OPERATION(task->command) == SGE_GDI_ADD) ||
           (SGE_GDI_GET_OPERATION(task->command) == SGE_GDI_COPY))) {
          lListElem *job, *next_job;

          next_job = lLast(task->data_list);
          while (ret && ((job = next_job) != NULL)) {
             next_job = lNext(job);

             lDechainElem(task->data_list, job);
             ret &= jsv_do_verify(ctx, JSV_CONTEXT_CLIENT, &job, answer_list, false);
             lInsertElem(task->data_list, NULL, job);
          }
       }
    }

   /* 
    * pack packet into packbuffer
    */ 
   /* 
    * EB: TODO: dry run necessary to calculate initial buffer size?  
    *    JG told me during review of ST that it might be possible that
    *    the dry run to calculate the buffer size might be slower
    *    than direcly packing. RD might have done tests...
    */
   if (ret) {
      size_t size = sge_gdi_packet_get_pb_size(packet);

      if (size > 0) {
         int pack_ret;

         pack_ret = init_packbuffer(&pb, size, 0);
         if (pack_ret != PACK_SUCCESS) {
            SGE_ADD_MSG_ID(sprintf(SGE_EVENT, "unable to prepare packbuffer for sending request"));
            ret = false;
         } else {
            pb_initialized = true;
         }
      }
   }
   if (ret) {
      ret = sge_gdi_packet_pack(packet, answer_list, &pb);
   }

   /* 
    * send packbuffer to master. keep care that user does not see
    * commlib related error messages if master is not up and running
    */
   if (ret) {
      const char *commproc = prognames[QMASTER];
      const char *host = ctx->get_master(ctx, false);
      int id = 1;
      int response_id = 0;
      commlib_error = sge_gdi2_send_any_request(ctx, 0, &message_id, host, commproc, id, &pb,
                                                TAG_GDI_REQUEST, response_id, NULL);
      if (commlib_error != CL_RETVAL_OK) {
         ret = false;
         commlib_error = ctx->is_alive(ctx);
         if (commlib_error != CL_RETVAL_OK) {
            u_long32 sge_qmaster_port = ctx->get_sge_qmaster_port(ctx);
            const char *mastername = ctx->get_master(ctx, true);

            if (commlib_error == CL_RETVAL_CONNECT_ERROR ||
                commlib_error == CL_RETVAL_CONNECTION_NOT_FOUND ) {
               /* For the default case, just print a simple message */
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_GDI_UNABLE_TO_CONNECT_SUS,
                                      prognames[QMASTER], sge_u32c(sge_qmaster_port),
                                      mastername?mastername:"<NULL>"));            
            } else { 
               /* For unusual errors, give more detail */
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_GDI_CANT_SEND_MSG_TO_PORT_ON_HOST_SUSS,
                                      prognames[QMASTER], sge_u32c(sge_qmaster_port),
                                      mastername?mastername:"<NULL>", 
                                      cl_get_error_text(commlib_error))); 
            }
         } else {
            SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_GDI_SENDINGGDIREQUESTFAILED));
         }
         answer_list_add(answer_list, SGE_EVENT, STATUS_NOQMASTER, ANSWER_QUALITY_ERROR);
         ret = false;
      }

   }

   /* after this point we do no longer need pb - free its resources */
   if (pb_initialized == true) {
      clear_packbuffer(&pb);
   }

   /* 
    * wait for response from master; also here keep care that commlib
    * related error messages are hidden if master is not up and running anymore
    */
   if (ret) {
      const char *commproc = prognames[QMASTER];
      const char *host = ctx->get_master(ctx, false);
      char rcv_host[CL_MAXHOSTLEN+1];
      char rcv_commproc[CL_MAXHOSTLEN+1];
      int tag = TAG_GDI_REQUEST;
      u_short id = 1;
      int gdi_error = CL_RETVAL_OK;
      int runs = 0;
      int retries = 0;
      bool do_ping = false;

      strcpy(rcv_host, host);
      strcpy(rcv_commproc, commproc);

      /*running this loop as long as configured in gdi_retries, doing a break after getting a gdi_request*/
      do {
         gdi_error = sge_gdi2_get_any_request(ctx, rcv_host, rcv_commproc, &id, &rpb, &tag, 
                                              true, message_id, NULL);

         do_ping = get_cl_ping_value();
         retries = get_gdi_retries_value();

         if (gdi_error == CL_RETVAL_OK) {
            /*no error happened, leaving while*/
            ret = true;
            break;
         } else {
            ret = false;
            /*this error appears, if qmaster or any qmaster thread is not responding, or overloaded*/
            if (gdi_error == CL_RETVAL_SYNC_RECEIVE_TIMEOUT) {
               cl_com_SIRM_t* cl_endpoint_status = NULL;
               cl_com_handle_t* handle = NULL;
               DPRINTF(("TEST_2372_OUTPUT: CL_RETVAL_SYNC_RECEIVE_TIMEOUT: RUNS="sge_U32CFormat"\n", sge_u32c(runs)));

               handle = ctx->get_com_handle(ctx);
               if (handle != NULL) {
                  DPRINTF(("TEST_2372_OUTPUT: GDI_TIMEOUT="sge_U32CFormat"\n", sge_u32c(handle->synchron_receive_timeout)));
               }
               if (do_ping == true) {
                  DPRINTF(("TEST_2372_OUTPUT: CL_PING=TRUE\n"));
                  cl_commlib_get_endpoint_status(handle, rcv_host, rcv_commproc, id, &cl_endpoint_status);
                  if (cl_endpoint_status != NULL) {
                     if (cl_endpoint_status->application_status != 0) {
                        DPRINTF(("TEST_2372_OUTPUT: QPING: error\n"));
                     } else {
                        DPRINTF(("TEST_2372_OUTPUT: QPING: ok\n"));
                     }
                     cl_com_free_sirm_message(&cl_endpoint_status);
                  } else {
                     DPRINTF(("TEST_2372_OUTPUT: QPING: failed\n"));
                     break;
                  }
               } else {
                  DPRINTF(("TEST_2372_OUTPUT: CL_PING=FALSE\n"));
               }
            } else {
               break;
            }
         }
         /* only increment runs if retries != -1 (-1 means retry forever) */
      } while (retries == -1 || runs++ < retries);
      
      if (ret == false) {
         commlib_error = ctx->is_alive(ctx);
         if (commlib_error != CL_RETVAL_OK) {
            u_long32 sge_qmaster_port = ctx->get_sge_qmaster_port(ctx);
            const char *mastername = ctx->get_master(ctx, true);

            if (commlib_error == CL_RETVAL_CONNECT_ERROR ||
                commlib_error == CL_RETVAL_CONNECTION_NOT_FOUND ) {
               /* For the default case, just print a simple message */
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_GDI_UNABLE_TO_CONNECT_SUS,
                                      prognames[QMASTER], sge_u32c(sge_qmaster_port),
                                      mastername?mastername:"<NULL>"));            
            } else { 
               /* For unusual errors, give more detail */
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_GDI_CANT_SEND_MSG_TO_PORT_ON_HOST_SUSS,
                                      prognames[QMASTER], sge_u32c(sge_qmaster_port),
                                      mastername?mastername:"<NULL>", 
                                      cl_get_error_text(commlib_error))); 
            }
         } else {
            SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_GDI_RECEIVEGDIREQUESTFAILED_US,
                    sge_u32c(message_id),
                    cl_get_error_text(gdi_error)));
         }
         answer_list_add(answer_list, SGE_EVENT, STATUS_NOQMASTER, ANSWER_QUALITY_ERROR);
      }
   }

   /* 
    * unpack result. the returned packet contains data and/or answer lists 
    */
   if (ret) {
      ret = sge_gdi_packet_unpack(&ret_packet, answer_list, &rpb);
      clear_packbuffer(&rpb); 
   }
  
   /* 
    * consistency check of received data:
    *    - is the packet id the same
    *    - does it contain the same amount of tasks
    *    - is the task sequence and the task id of each received task the same
    */
   if (ret) {
      sge_gdi_task_class_t *send;
      sge_gdi_task_class_t *recv;
      bool gdi_mismatch = false;

      if (packet->id != ret_packet->id) {
         gdi_mismatch = true;
      }

      send = packet->first_task;
      recv = ret_packet->first_task;
      while (send != NULL && recv != NULL) {
         if (send->id == recv->id) {
            lFreeList(&send->data_list);
            send->data_list = recv->data_list;
            send->answer_list = recv->answer_list;
            recv->data_list = NULL;
            recv->answer_list = NULL;
         } else {
            gdi_mismatch = true;
            break;
         }
         send = send->next;
         recv = recv->next;
      }
      if (send != NULL || recv != NULL) {
         gdi_mismatch = true;
      }
      if (gdi_mismatch) {
         /* For unusual errors, give more detail */
         SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_GDI_MISMATCH_SEND_RECEIVE));
         answer_list_add(answer_list, SGE_EVENT, STATUS_NOQMASTER, ANSWER_QUALITY_ERROR);
         ret = false;
      }
   }

#ifdef KERBEROS
   /* clear the forward TGT request */
   if (ret && state->first->target == SGE_JB_LIST &&
       SGE_GDI_GET_OPERATION(packet->first_task->command) == SGE_GDI_ADD) {
      krb_set_client_flags(krb_get_client_flags() & ~KRB_FORWARD_TGT);
      krb_set_tgt_id(0);
   }
#endif

   sge_gdi_packet_free(&ret_packet);
   DRETURN(ret);
}

/****** gdi/request_internal/sge_gdi_packet_execute_internal() ****************
*  NAME
*     sge_gdi_packet_execute_internal() -- execute a GDI packet 
*
*  SYNOPSIS
*     bool 
*     sge_gdi_packet_execute_internal(sge_gdi_ctx_class_t* ctx, 
*                                     lList **answer_list, 
*                                     sge_gdi_packet_class_t *packet) 
*
*  FUNCTION
*     This functions stores a GDI "packet" in the "Master_Packet_Queue"
*     so that it will be executed in future. This function can only
*     be used in the context of an internal GDI client (thread in 
*     qmaster). 
*
*     To send packets from external clients 
*     the function sge_gdi_packet_execute_external() has to be used.
*
*     Please note that in contrast to sge_gdi_packet_execute_external()
*     this function does not assures that the GDI request contained in the
*     "packet" is already executed after this function returns.
*
*     sge_gdi_packet_wait_for_result_internal() has to be called to 
*     assure this. This function will also creates a GDI multi answer 
*     lists structure from the information contained in the handled
*     packet after this function has been called.
*
*     
*  INPUTS
*     sge_gdi_ctx_class_t* ctx       - context handle 
*     lList **answer_list            - answer list 
*     sge_gdi_packet_class_t *packet - packet 
*
*  RESULT
*     bool - error state
*        true   - success
*        false  - error (answer_lists will contain details)
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_execute_extern() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/sge_gdi_packet_execute_external() 
*     gdi/request_internal/sge_gdi_packet_execute_internal() 
*     gdi/request_internal/sge_gdi_packet_wait_for_result_external()
*     gdi/request_internal/sge_gdi_packet_wait_for_result_internal()
*******************************************************************************/
bool 
sge_gdi_packet_execute_internal(sge_gdi_ctx_class_t* ctx, lList **answer_list, 
                                sge_gdi_packet_class_t *packet) 
{
   bool ret = true;

   DENTER(TOP_LAYER, "sge_gdi_packet_execute_internal");

   /* 
    * here the packet gets a unique request id and source for host
    * user and group is initialized
    */
   packet->id = gdi_state_get_next_request_id();
   packet->commproc = strdup(prognames[QMASTER]);      
   packet->host = strdup(ctx->get_master(ctx, false));
   packet->is_intern_request = true;

   ret = sge_gdi_packet_parse_auth_info(packet, &(packet->first_task->answer_list),
                                        &(packet->uid), packet->user, sizeof(packet->user),
                                        &(packet->gid), packet->group, sizeof(packet->group));

   /* 
    * append the packet to the packet list of the worker threads
    */
   sge_tq_store_notify(Master_Task_Queue, SGE_TQ_GDI_PACKET, packet);

   DRETURN(ret);
}

/****** gdi/request_internal/sge_gdi_packet_wait_for_result_external() ******
*  NAME
*     sge_gdi_packet_wait_for_result_external() -- wait for packet result 
*
*  SYNOPSIS
*     bool 
*     sge_gdi_packet_wait_for_result_external(sge_gdi_ctx_class_t* ctx, 
*                                             lList **answer_list, 
*                                             sge_gdi_packet_class_t *packet, 
*                                             lList **malpp) 
*
*  FUNCTION
*     Despite to its name this function does not wait. This is not necessary
*     because the GDI request handled in the execution process previously
*     is already done. A call to this function simply breates a GDI multi 
*     answer list.
*
*  INPUTS
*     sge_gdi_ctx_class_t* ctx        - context handle 
*     lList **answer_list             - answer list 
*     sge_gdi_packet_class_t **packet - GDI packet 
*     lList **malpp                   - multi answer list 
*
*  RESULT
*     bool - error state
*        true  - success
*        false - error
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_wait_for_result_external() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/sge_gdi_packet_execute_external() 
*     gdi/request_internal/sge_gdi_packet_execute_internal() 
*     gdi/request_internal/sge_gdi_packet_wait_for_result_external()
*     gdi/request_internal/sge_gdi_packet_wait_for_result_internal()
*******************************************************************************/
bool 
sge_gdi_packet_wait_for_result_external(sge_gdi_ctx_class_t* ctx, lList **answer_list,
                                        sge_gdi_packet_class_t **packet, lList **malpp)
{
   bool ret = true;

   DENTER(TOP_LAYER, "sge_gdi_packet_wait_for_result_extern");

   /* 
    * The packet itself has already be executed in sge_gdi_packet_execute_external() 
    * so it is only necessary to create the muti answer and do cleanup
    */
   ret = sge_gdi_packet_create_multi_answer(ctx, answer_list, packet, malpp);

   DRETURN(ret);
}

/****** gdi/request_internal/sge_gdi_packet_wait_for_result_internal() ******
*  NAME
*     sge_gdi_packet_wait_for_result_internal() -- wait for handled packet 
*
*  SYNOPSIS
*     bool 
*     sge_gdi_packet_wait_for_result_internal(sge_gdi_ctx_class_t* ctx, 
*                                             lList **answer_list, 
*                                             sge_gdi_packet_class_t *packet, 
*                                             lList **malpp) 
*
*  FUNCTION
*     This function can only be called in a qmaster thread. Then
*     this function blocks until the GDI packet, which has to be
*     given to qmaster via sge_gdi_packet_execute_internal(), is
*     executed completely (either successfull or with errors). 
*
*     After that it creates a multi answer list.
*
*  INPUTS
*     sge_gdi_ctx_class_t* ctx        - context handle 
*     lList **answer_list             - answer list 
*     sge_gdi_packet_class_t **packet - GDI packet 
*     lList **malpp                   - multi answer list 
*
*  RESULT
*     bool - error state
*        true  - success
*        false - error
*
*  NOTES
*     MT-NOTE: sge_gdi_packet_wait_for_result_internal() is MT safe 
*
*  SEE ALSO
*     gdi/request_internal/sge_gdi_packet_execute_external() 
*     gdi/request_internal/sge_gdi_packet_execute_internal() 
*     gdi/request_internal/sge_gdi_packet_wait_for_result_external()
*     gdi/request_internal/sge_gdi_packet_wait_for_result_internal()
*******************************************************************************/
bool 
sge_gdi_packet_wait_for_result_internal(sge_gdi_ctx_class_t* ctx, lList **answer_list,
                                        sge_gdi_packet_class_t **packet, lList **malpp)
{
   bool ret = true;

   DENTER(TOP_LAYER, "sge_gdi_packet_wait_for_result_internal");

   /* 
    * wait for response from worker thread that the packet is handled
    */
   sge_gdi_packet_wait_till_handled(*packet);

   /*
    * create the multi answer and destroy the packet
    */
   ret = sge_gdi_packet_create_multi_answer(ctx, answer_list, packet, malpp);

   DRETURN(ret);
}

