/*___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 <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <pwd.h>

#include "sge.h"
#include "sge_log.h"
#include "sgermon.h"
#include "commlib.h"
#include "sge_conf.h"
#include "configuration_qmaster.h"
#include "cull.h"
#include "config_file.h"
#include "sge_userset_qmaster.h"
#include "sge_answer.h"
#include "sge_utility.h"
#include "sge_userprj_qmaster.h"
#include "sge_parse_num_par.h"
#include "setup_path.h"
#include "sge_event_master.h"
#include "sge_string.h"
#include "reschedule.h"
#include "sge_unistd.h"
#include "sge_hostname.h"
#include "sge_host.h"
#include "sge_prog.h"
#include "sge_uidgid.h" 
#include "sge_spool.h"
#include "sge_userprj.h"
#include "sge_userset.h"
#include "sge_persistence_qmaster.h"

#include "lck/sge_lock.h"
#include "lck/sge_mtutil.h"

#include "spool/sge_spooling.h"

#include "sgeobj/sge_path_alias.h"
#include "sgeobj/sge_jsv.h"

#include "msg_common.h"
#include "msg_qmaster.h"



/* Static configuration entries may be changed at runtime with a warning */
static char *Static_Conf_Entries[] = { "execd_spool_dir", NULL };


static int check_config(lList **alpp, lListElem *conf);
static int do_mod_config(sge_gdi_ctx_class_t *ctx, char *aConfName, lListElem *anOldConf, lListElem *aNewConf, lList**anAnswer);
static lListElem* is_reprioritize_missing(lList *theOldConfEntries, lList *theNewConfEntries);
static int check_static_conf_entries(lList *theOldConfEntries, lList *theNewConfEntries, lList **anAnswer);
static int exchange_conf_by_name(sge_gdi_ctx_class_t *ctx, char *aConfName, lListElem *anOldConf, lListElem *aNewConf, lList**anAnswer);
static bool has_reschedule_unknown_change(lList *theOldConfEntries, lList *theNewConfEntries);
static int do_add_config(sge_gdi_ctx_class_t *ctx, char *aConfName, lListElem *aConf, lList**anAnswer);
static int remove_conf_by_name(char *aConfName);
static lListElem *get_entry_from_conf(lListElem *aConf, const char *anEntryName);
static u_long32 sge_get_config_version_for_host(const char* aName);

/* 
 * Read the cluster configuration from secondary storage using 'aSpoolContext'.
 * This is the bootstrap function for the configuration module. It does populate
 * the list with the cluster configuration.
 */
int sge_read_configuration(sge_gdi_ctx_class_t *ctx, lListElem *aSpoolContext, lList **config_list, lList *anAnswer)
{
   lListElem *local = NULL;
   lListElem *global = NULL;
   int ret = -1;
   const char *cell_root = ctx->get_cell_root(ctx);
   const char *qualified_hostname = ctx->get_qualified_hostname(ctx);
   u_long32 progid = ctx->get_who(ctx);

   DENTER(TOP_LAYER, "sge_read_configuration");
   
   SGE_LOCK(LOCK_MASTER_CONF, LOCK_WRITE);

   spool_read_list(&anAnswer, aSpoolContext, config_list, SGE_TYPE_CONFIG);

   /*
    * For Urubu (6.2u2) we won't have and update script. Therefore the master
    * has to be able to cope with a missing "jsv_url" string. 
    *
    * TODO: Nethertheless we have to add the "jsv_url" to the update script
    *       for the first release after Urubu.
    */
   {
      lListElem *global = lGetElemHost(*config_list, CONF_name, "global");

      if (global != NULL) {
         lList *entries = lGetList(global, CONF_entries);
         lListElem *jsv_url = lGetElemStr(entries, CF_name, "jsv_url");

         if (jsv_url == NULL) {
            jsv_url = lAddSubStr(global, CF_name, "jsv_url", CONF_entries, CF_Type);
            if (jsv_url != NULL) { 
               lSetString(jsv_url, CF_value, "none");
            }
         }
      }
   }
   /*
    * For Urubu (6.2u2) we won't have and update script. Therefore the master
    * has to be able to cope with a missing "jsv_allowed_mod" string. 
    *
    * TODO: Nethertheless we have to add the "jsv_allowed_mod" to the update 
    *       script for the first release after Urubu.
    */
   {
      lListElem *global = lGetElemHost(*config_list, CONF_name, "global");

      if (global != NULL) {
         lList *entries = lGetList(global, CONF_entries);
         lListElem *jsv_url = lGetElemStr(entries, CF_name, "jsv_allowed_mod");

         if (jsv_url == NULL) {
            jsv_url = lAddSubStr(global, CF_name, "jsv_allowed_mod", CONF_entries, CF_Type);
            if (jsv_url != NULL) { 
               lSetString(jsv_url, CF_value, "ac,h,i,e,o,j,M,N,p,w");
            }
         }
      }
   }
   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_WRITE);
 
   answer_list_output(&anAnswer);

   DPRINTF(("qualified_hostname: '%s'\n", qualified_hostname));
   if ((local = sge_get_configuration_for_host(qualified_hostname)) == NULL) {
      /* write a warning into messages file, if no local config exists*/
      WARNING((SGE_EVENT, MSG_CONFIG_NOLOCAL_S, qualified_hostname));
   }

   if ((global = sge_get_configuration_for_host(SGE_GLOBAL_NAME)) == NULL) {
      ERROR((SGE_EVENT, MSG_CONFIG_NOGLOBAL));
      DRETURN(-1);
   }

   ret = merge_configuration(&anAnswer, progid, cell_root, global, local, NULL);
   answer_list_output(&anAnswer);

   lFreeElem(&local);
   lFreeElem(&global);

   if (0 != ret) {
      ERROR((SGE_EVENT, MSG_CONFIG_ERRORXMERGINGCONFIGURATIONY_IS, ret, qualified_hostname));
      DRETURN(-1);
   }

   sge_show_conf();         

   DRETURN(0);
}

/*
 * Delete configuration 'confp' from cluster configuration.
 *
 * TODO: A fix for IZ issue #79 is needed. For this to be done it may be 
 * necessary to introduce something like 'protected' configuration entries.
 */
int 
sge_del_configuration(sge_gdi_ctx_class_t *ctx,
                      lListElem *aConf, lList **anAnswer, 
                      char *aUser, char *aHost)
{
   const char *tmp_name = NULL;
   char unique_name[CL_MAXHOSTLEN];
   int ret = -1;

   DENTER(TOP_LAYER, "sge_del_configuration");

   if (!aConf || !aUser || !aHost) {
      CRITICAL((SGE_EVENT, MSG_SGETEXT_NULLPTRPASSED_S, SGE_FUNC));
      answer_list_add(anAnswer, SGE_EVENT, 
                      STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR);
      DRETURN(STATUS_EUNKNOWN);
   }
   
   if ((tmp_name = lGetHost(aConf, CONF_name)) == NULL) {
      CRITICAL((SGE_EVENT, MSG_SGETEXT_MISSINGCULLFIELD_SS, 
                lNm2Str(CONF_name), SGE_FUNC));
      answer_list_add(anAnswer, SGE_EVENT, 
                      STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR);
      DRETURN(STATUS_EUNKNOWN);
   }

   /*
    * Due to CR 6319231 IZ 1760:
    *    try to resolve the hostname
    *    if it is not resolveable then
    *       ignore this and use the hostname stored in the configuration obj
    *       or use the given name if no object can be found
    */
   ret = sge_resolve_hostname(tmp_name, unique_name, EH_name);
   if (ret != CL_RETVAL_OK) {
      lListElem *conf_obj = NULL;
 
      DPRINTF(("%s: error %s resolving host %s\n", SGE_FUNC,
               cl_get_error_text(ret), tmp_name));
 
      conf_obj = sge_get_configuration_for_host(tmp_name);
      if (conf_obj != NULL) {
         DPRINTF(("using hostname stored in configuration object\n"));
         strcpy(unique_name, lGetHost(conf_obj, CONF_name));
      } else {
         ERROR((SGE_EVENT, MSG_SGETEXT_CANT_DEL_CONFIG2_S, tmp_name));
         answer_list_add(anAnswer, SGE_EVENT,
                         STATUS_EEXIST, ANSWER_QUALITY_ERROR);
         DRETURN(STATUS_EEXIST);
      }
   }

   /* Do not allow to delete global configuration */
   if (!strcasecmp(SGE_GLOBAL_NAME, unique_name)) {
      ERROR((SGE_EVENT, MSG_SGETEXT_CANT_DEL_CONFIG_S, unique_name));
      answer_list_add(anAnswer, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
      DRETURN(STATUS_EEXIST);
   }

   sge_event_spool(ctx, anAnswer, 0, sgeE_CONFIG_DEL, 0, 0, unique_name, NULL, NULL, NULL, NULL, NULL, true, true);
    
   remove_conf_by_name(unique_name);
   
   INFO((SGE_EVENT, MSG_SGETEXT_REMOVEDFROMLIST_SSSS, aUser, aHost, unique_name, MSG_OBJ_CONF ));
   answer_list_add(anAnswer, SGE_EVENT, STATUS_OK, ANSWER_QUALITY_INFO);
   
   update_reschedule_unknown_timout_values(unique_name);

   /* invalidate cached configuration values */
   mconf_set_new_config(true);
    
   DRETURN(STATUS_OK);
}


/****** qmaster/sge_mod_configuration() ****************************************
*  NAME
*     sge_mod_configuration() -- modify cluster configuration
*
*  SYNOPSIS
*     int sge_mod_configuration(lListElem *aConf, lList **anAnswer, char *aUser,
*                               char *aHost)
*
*  FUNCTION
*     Modify cluster configuration. 'confp' is a pointer to a 'CONF_Type' list
*     element and does contain the modified configuration entry. Adding a new
*     configuration entry is also viewed as a modification.
*
*  INPUTS
*     lListElem *aConf  - CONF_Type element containing the modified conf
*     lList **anAnswer  - answer list
*     char *aUser       - target user
*     char *aHost       - target host
*
*  RESULT
*     int - 0 success
*          -1 error
*
*  NOTES
*     MT-NOTE: sge_mod_configuration() is MT safe 
*
*******************************************************************************/
int sge_mod_configuration(sge_gdi_ctx_class_t *ctx, lListElem *aConf, lList **anAnswer, char *aUser, char *aHost)
{
   lListElem *old_conf;
   const char *tmp_name = NULL;
   char unique_name[CL_MAXHOSTLEN];
   int ret = -1;
   const char *cell_root = ctx->get_cell_root(ctx);
   const char *qualified_hostname = ctx->get_qualified_hostname(ctx);
   u_long32 progid = ctx->get_who(ctx);

   DENTER(TOP_LAYER, "sge_mod_configuration");

   if (!aConf || !aUser || !aHost) {
      CRITICAL((SGE_EVENT, MSG_SGETEXT_NULLPTRPASSED_S, SGE_FUNC));
      answer_list_add(anAnswer, SGE_EVENT, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR);
      DRETURN(STATUS_EUNKNOWN);
   }

   if ((tmp_name = lGetHost(aConf, CONF_name)) == NULL) {
      CRITICAL((SGE_EVENT, MSG_SGETEXT_MISSINGCULLFIELD_SS, lNm2Str(CONF_name), SGE_FUNC));
      answer_list_add(anAnswer, SGE_EVENT, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR);
      DRETURN(STATUS_EUNKNOWN);
   }

   if ((ret = sge_resolve_hostname(tmp_name, unique_name, EH_name)) != CL_RETVAL_OK) {
      DPRINTF(("%s: error %s resolving host %s\n", SGE_FUNC, cl_get_error_text(ret), tmp_name));
      ERROR((SGE_EVENT, MSG_SGETEXT_CANTRESOLVEHOST_S, tmp_name));
      answer_list_add(anAnswer, SGE_EVENT, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR);
      DRETURN(STATUS_EUNKNOWN);
   }
   
   if ((ret = check_config(anAnswer, aConf))) {
      DRETURN(ret); 
   }

   if ((old_conf = sge_get_configuration_for_host(unique_name)) != NULL) {
      int ret = -1;
      
      ret = do_mod_config(ctx, unique_name, old_conf, aConf, anAnswer);
      
      lFreeElem(&old_conf);
      
      if (ret == 0) {    
         INFO((SGE_EVENT, MSG_SGETEXT_MODIFIEDINLIST_SSSS, aUser, aHost, unique_name, MSG_OBJ_CONF));
         answer_list_add(anAnswer, SGE_EVENT, STATUS_OK, ANSWER_QUALITY_INFO);
      } else {
         DRETURN(STATUS_EUNKNOWN);
      }
   } else {
      do_add_config(ctx, unique_name, aConf, anAnswer);
            
      INFO((SGE_EVENT, MSG_SGETEXT_ADDEDTOLIST_SSSS, aUser, aHost, unique_name, MSG_OBJ_CONF));            
      answer_list_add(anAnswer, SGE_EVENT, STATUS_OK, ANSWER_QUALITY_INFO);
   }
   
   if (strcmp(SGE_GLOBAL_NAME, unique_name) == 0) {
      sge_add_event(0, sgeE_GLOBAL_CONFIG, 0, 0, NULL, NULL, NULL, NULL);
   }

   /*
   ** is the configuration change relevant for the qmaster itsself?
   ** if so, initialise conf struct anew
   */
   if (strcmp(unique_name, SGE_GLOBAL_NAME) == 0 || sge_hostcmp(unique_name, qualified_hostname) == 0) {
      lListElem *local = NULL;
      lListElem *global = NULL;
      lList *answer_list = NULL;
      char* qmaster_params = NULL;
      int accounting_flush_time = mconf_get_accounting_flush_time();

      if ((local = sge_get_configuration_for_host(qualified_hostname)) == NULL) {
         WARNING((SGE_EVENT, MSG_CONFIG_NOLOCAL_S, qualified_hostname));
      }
      
      if ((global = sge_get_configuration_for_host(SGE_GLOBAL_NAME)) == NULL) {
         ERROR((SGE_EVENT, MSG_CONFIG_NOGLOBAL));
      }
            
      if (merge_configuration(&answer_list, progid, cell_root, global, local, NULL) != 0) {
         ERROR((SGE_EVENT, MSG_CONF_CANTMERGECONFIGURATIONFORHOST_S, qualified_hostname));
      }
      answer_list_output(&answer_list);

      /* Restart the accounting flush event if needed. */
      if ((accounting_flush_time == 0) &&
          (mconf_get_accounting_flush_time() != 0)) {
         te_event_t ev = te_new_event(time(NULL), TYPE_ACCOUNTING_TRIGGER, ONE_TIME_EVENT, 1, 0, NULL);
         te_add_event(ev);
         te_free_event(&ev);
      }
      
      lFreeElem(&local);
      lFreeElem(&global);
      
      sge_show_conf();

      /* 'max_unheard' may have changed */
      cl_commlib_set_connection_param(cl_com_get_handle("qmaster", 1), HEARD_FROM_TIMEOUT, mconf_get_max_unheard());

      /* fetching qmaster_params and begin to parse */
      qmaster_params = mconf_get_qmaster_params();

      /* updating the commlib paramterlist and gdi_timeout with new or changed parameters */
      cl_com_update_parameter_list(qmaster_params);

      FREE(qmaster_params);
   }
    
   /* invalidate configuration cache */
   mconf_set_new_config(true);
   
   DRETURN(STATUS_OK);
}
   
static int check_config(lList **alpp, lListElem *conf)
{
   lListElem *ep;
   const char *name, *value;
   const char *conf_name;
 
   DENTER(TOP_LAYER, "check_config");
 
   conf_name = lGetHost(conf, CONF_name);
 
   for_each(ep, lGetList(conf, CONF_entries)) {
      name = lGetString(ep, CF_name);
      value = lGetString(ep, CF_value);
 
      if (name == NULL) {
         ERROR((SGE_EVENT, MSG_CONF_NAMEISNULLINCONFIGURATIONLISTOFX_S,
               conf_name));
         answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
         DRETURN(STATUS_EEXIST);
      }
      if (value == NULL) {
         ERROR((SGE_EVENT, MSG_CONF_VALUEISNULLFORATTRXINCONFIGURATIONLISTOFY_SS,
                name, conf_name));
         answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
         DRETURN(STATUS_EEXIST);
      }

      if (!strcmp(name, "loglevel")) {
         u_long32 tmp_uval;
         if (sge_parse_loglevel_val(&tmp_uval, value) != 1) {
            ERROR((SGE_EVENT, MSG_CONF_GOTINVALIDVALUEXFORLOGLEVEL_S, value));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }
      } else if (strcmp(name, "jsv_url") == 0) {
         if (strcasecmp("none", value) != 0) {
            dstring input = DSTRING_INIT;
            dstring type = DSTRING_INIT;
            dstring user = DSTRING_INIT;
            dstring path = DSTRING_INIT;
            bool lret = true;

            sge_dstring_append(&input, value);
            lret = jsv_url_parse(&input, alpp, &type, &user, &path, false); 
            sge_dstring_free(&input);
            sge_dstring_free(&type);
            sge_dstring_free(&user);
            sge_dstring_free(&path);
            if (!lret) {
               /* answer is written by jsv_url_parse */
               DRETURN(STATUS_EEXIST);
            }
         } 
      } else if (!strcmp(name, "shell_start_mode")) {
         if ((strcasecmp("unix_behavior", value) != 0) && 
             (strcasecmp("posix_compliant", value) != 0) &&
             (strcasecmp("script_from_stdin", value) != 0) ) {
            ERROR((SGE_EVENT, MSG_CONF_GOTINVALIDVALUEXFORSHELLSTARTMODE_S, value));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }
      } else if (!strcmp(name, "shell")) {
         if (!path_verify(name, alpp, "shell", true)) {
            ERROR((SGE_EVENT, MSG_CONF_GOTINVALIDVALUEXFORSHELL_S, value));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }
      } else if (!strcmp(name, "load_report_time")) {
         /* do not allow infinity entry for load_report_time */
         if (strcasecmp(value, "infinity") == 0) {
            ERROR((SGE_EVENT, MSG_CONF_INFNOTALLOWEDFORATTRXINCONFLISTOFY_SS, name, conf_name));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }
      } else if (!strcmp(name, "max_unheard")) {
         /* do not allow infinity entry */
         if (strcasecmp(value,"infinity") == 0) {
            ERROR((SGE_EVENT, MSG_CONF_INFNOTALLOWEDFORATTRXINCONFLISTOFY_SS, name, conf_name));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }
      } else if (!strcmp(name, "admin_user")) {
         struct passwd pw_struct;
         char *buffer;
         int size;

         size = get_pw_buffer_size();
         buffer = sge_malloc(size);
         if (strcasecmp(value, "none") && !sge_getpwnam_r(value, &pw_struct, buffer, size)) {
            ERROR((SGE_EVENT, MSG_CONF_GOTINVALIDVALUEXASADMINUSER_S, value));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            FREE(buffer);
            DRETURN(STATUS_EEXIST);
         }
         FREE(buffer);
      } else if (!strcmp(name, "user_lists")||!strcmp(name, "xuser_lists")) {
         lList *tmp = NULL;
         int ok;

         /* parse just for .. */ 
         if (lString2ListNone(value, &tmp, US_Type, US_name, " \t,")) {
            ERROR((SGE_EVENT, MSG_CONF_FORMATERRORFORXINYCONFIG_SS, name, conf_name));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }

         /* .. checking userset names */
         ok = (userset_list_validate_acl_list(tmp, alpp) == STATUS_OK);
         lFreeList(&tmp);
         if (!ok) {
            DRETURN(STATUS_EEXIST);
         }
      } else if (!strcmp(name, "projects") || !strcmp(name, "xprojects")) {
         lList *tmp = NULL;
         int ok=1;

         /* parse just for .. */ 
         if (lString2ListNone(value, &tmp, PR_Type, PR_name, " \t,")) {
            ERROR((SGE_EVENT, MSG_CONF_FORMATERRORFORXINYCONFIG_SS, name, conf_name));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }

         /* .. checking project names */
         ok = (verify_project_list(alpp, tmp, *object_type_get_master_list(SGE_TYPE_PROJECT),
                    name, "configuration", conf_name)==STATUS_OK);
         lFreeList(&tmp);
         if (!ok) {
            DRETURN(STATUS_EEXIST);
         }
      } else if (!strcmp(name, "prolog") || !strcmp(name, "epilog")) {
         if (strcasecmp(value, "none")) {
            const char *t, *script = value;

            /* skip user name */
            if ((t = strpbrk(script, "@ ")) && *t == '@')
               script = &t[1];

            /* force use of absolute paths if string <> none */
            if (script[0] != '/' ) {
               ERROR((SGE_EVENT, MSG_CONF_THEPATHGIVENFORXMUSTSTARTWITHANY_S, name));
               answer_list_add(alpp, SGE_EVENT, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR);
               DRETURN(STATUS_EEXIST);
            }
   
            /* ensure that variables are valid */
            if (replace_params(script, NULL, 0, prolog_epilog_variables)) {
               ERROR((SGE_EVENT, MSG_CONF_PARAMETERXINCONFIGURATION_SS, name, err_msg));
               answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
               DRETURN(STATUS_EEXIST);
            }
         }
      } else if (!strcmp(name, "auto_user_oticket") || !strcmp(name, "auto_user_fshare")) {
         u_long32 uval = 0;
         if (!extended_parse_ulong_val(NULL, &uval, TYPE_INT, value, NULL, 0, 0, true)) {
            ERROR((SGE_EVENT, MSG_CONF_FORMATERRORFORXINYCONFIG_SS, name, value ? value : "(NULL)"));
            answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR);
            DRETURN(STATUS_EEXIST);
         }
      }

      /* 
       * check paths, see also CR 6506580.
       * The following must be none or a valid absolute path:
       * - load_sensor
       * - set_token_cmd
       * - pag_cmd
       * - shepherd_cmd
       *
       * The following must be a valid absolute path:
       * - mailer
       * - xterm
       * - *_daemon, may also be "builtin"
       */
      else if (strcmp(name, "set_token_cmd") == 0 ||
          strcmp(name, "pag_cmd") == 0 ||
          strcmp(name, "shepherd_cmd") == 0) {
         if (strcasecmp(value, "none") != 0) {
            if (!path_verify(value, alpp, name, true)) {
               answer_list_log(alpp, false, false);
               DRETURN(STATUS_EEXIST);
            }
         }
      } else if (strcmp(name, "mailer") == 0 ||
          strcmp(name, "xterm") == 0) {
         if (!path_verify(value, alpp, name, true)) {
            answer_list_log(alpp, false, false);
            DRETURN(STATUS_EEXIST);
         }
      } else if (strcmp(name, "qlogin_daemon") == 0 ||
          strcmp(name, "rlogin_daemon") == 0 ||
          strcmp(name, "rsh_daemon") == 0) {
         if (strcasecmp(value, "builtin") != 0) {
            if (!path_verify(value, alpp, name, true)) {
               answer_list_log(alpp, false, false);
               DRETURN(STATUS_EEXIST);
            }
         }
      }

      /* load_sensor is a comma separated list of scripts */
      else if (strcmp(name, "load_sensor") == 0 && strcasecmp(value, "none") != 0) {
         struct saved_vars_s *context = NULL;
         const char *path = sge_strtok_r(value, ",", &context);
         do {
            if (!path_verify(path, alpp, name, true)) {
               answer_list_log(alpp, false, false);
               sge_free_saved_vars(context);
               DRETURN(STATUS_EEXIST);
            }
         } while ((path = sge_strtok_r(NULL, ",", &context)) != NULL);
         sge_free_saved_vars(context);
      }
   }
 
   DRETURN(0);
}
  

/*
 * Compare configuration 'aConf' for host 'aHost' with the cluster configuration.
 * Return '0' if 'aConf' is equal to the cluster configuration, '1' otherwise.
 *
 * 'aHost' is of type 'EH_Type', 'aConf' is of type 'CONF_Type'.
 */
int sge_compare_configuration(lListElem *aHost, lList *aConf)
{
   lListElem *conf_entry = NULL;

   DENTER(TOP_LAYER, "sge_compare_configuration");

   if (lGetNumberOfElem(aConf) == 0) {
      DPRINTF(("%s: configuration for %s is empty\n", SGE_FUNC, lGetHost(aHost, EH_name)));
      DRETURN(1);
   }
   
   for_each(conf_entry, aConf) {
      const char *host_name = NULL;
      u_long32 conf_version;
      u_long32 master_version;
      
      host_name = lGetHost(conf_entry, CONF_name);
      master_version = sge_get_config_version_for_host(host_name); 
            
      conf_version = lGetUlong(conf_entry, CONF_version);
      
      if (master_version != conf_version) {
         DPRINTF(("%s: configuration for %s changed from version %ld to %ld\n", SGE_FUNC, host_name, master_version, conf_version));
         DRETURN(1);
      }
   }

   DRETURN(0);
}


/*
 * Return a *COPY* of configuration entry 'anEntryName'. First we do query the
 * local configuration 'aHost'. If that is fruitless, we try the global
 * configuration. 
 */
lListElem *sge_get_configuration_entry_by_name(const char *aHost, const char *anEntryName)
{
   lListElem *conf = NULL;
   lListElem *elem = NULL;
   
   DENTER(TOP_LAYER, "sge_get_configuration_entry_by_name");
   
   SGE_ASSERT((NULL != aHost) && (NULL != anEntryName));
   
   /* try local configuration first */
   if ((conf = sge_get_configuration_for_host(aHost)) != NULL) {
      elem = get_entry_from_conf(conf, anEntryName);
   }
   lFreeElem(&conf);
   
   /* local configuration did not work, try global one */
   if ((elem == NULL) && ((conf = sge_get_configuration_for_host(SGE_GLOBAL_NAME)) != NULL)) {
      elem = get_entry_from_conf(conf, anEntryName);
   }
  
   lFreeElem(&conf);
   DRETURN(elem);
}

static lListElem *get_entry_from_conf(lListElem *aConf, const char *anEntryName)
{
   lList *entries = NULL;
   lListElem *elem = NULL;
   
   entries = lGetList(aConf, CONF_entries);
   
   elem = lGetElemStr(entries, CF_name, anEntryName);
   
   return lCopyElem(elem);
}

/*
 * Return a *COPY* of the master configuration.
 */
lList* sge_get_configuration()
{
   lList *conf = NULL;
   object_description *object_base = object_type_get_object_description();
   lList *config_list = *(object_base[SGE_TYPE_CONFIG].list);
   
   DENTER(TOP_LAYER, "sge_get_configuration");

   SGE_LOCK(LOCK_MASTER_CONF, LOCK_READ);
   
   conf = lCopyListHash(lGetListName(config_list), config_list, false);

   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_READ);
   
   DRETURN(conf);
}

static u_long32 sge_get_config_version_for_host(const char* aName)
{
   lListElem *conf = NULL;
   u_long32 version = 0;
   char unique_name[CL_MAXHOSTLEN];
   int ret = -1;
   object_description *object_base = object_type_get_object_description();
   lList *config_list = *(object_base[SGE_TYPE_CONFIG].list);

   DENTER(TOP_LAYER, "sge_get_configuration_for_host");

   SGE_LOCK(LOCK_MASTER_CONF, LOCK_READ);
  
   conf = lGetElemHost(config_list, CONF_name, aName);
   if (conf == NULL) {
      /*
       * Due to CR 6319231 IZ 1760:
       *    Try to resolve the hostname
       *    if it is not resolveable then
       *       ignore this and use the given hostname
       */
      ret = sge_resolve_hostname(aName, unique_name, EH_name);
      if (CL_RETVAL_OK != ret) {
         DPRINTF(("%s: error %s resolving host %s\n", SGE_FUNC,
                 cl_get_error_text(ret), aName));
      }
      conf = lGetElemHost(config_list, CONF_name, unique_name);
   } 

   if (conf == NULL) {
      DPRINTF(("%s: no master configuration for %s found\n", SGE_FUNC, aName));
   } else {
      version = lGetUlong(conf, CONF_version);
   }
   
   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_READ);

   DRETURN(version);  
}

/*
 * Return a *COPY* of the configuration for host 'aName'. The host name 'aName'
 * will be resolved to eliminate any host name differences caused by the
 * various host name formats or the host name alias mechanism.
 */
lListElem* sge_get_configuration_for_host(const char* aName)
{
   lListElem *conf = NULL;
   char unique_name[CL_MAXHOSTLEN];
   int ret = -1;
   object_description *object_base = object_type_get_object_description();
   lList *config_list = *(object_base[SGE_TYPE_CONFIG].list);

   DENTER(TOP_LAYER, "sge_get_configuration_for_host");

   SGE_ASSERT((NULL != aName));

   /*
    * Due to CR 6319231 IZ 1760:
    *    Try to resolve the hostname
    *    if it is not resolveable then
    *       ignore this and use the given hostname
    */
   ret = sge_resolve_hostname(aName, unique_name, EH_name);
   if (CL_RETVAL_OK != ret) {
      DPRINTF(("%s: error %s resolving host %s\n", SGE_FUNC,
              cl_get_error_text(ret), aName));
      strcpy(unique_name, aName);
   }

   SGE_LOCK(LOCK_MASTER_CONF, LOCK_READ);
   
   conf = lCopyElem(lGetElemHost(config_list, CONF_name, unique_name));

   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_READ);

   DRETURN(conf);
}


void sge_set_conf_reprioritize(lListElem *aConf, bool aFlag)
{
   lList *entries = NULL;
   lListElem *ep = NULL;

   DENTER(TOP_LAYER, "sge_set_conf_reprioritize");

   SGE_ASSERT((NULL != aConf));

   entries = lGetList(aConf, CONF_entries);

   ep = lGetElemStr(entries, CF_name, REPRIORITIZE);

   if (NULL == ep) {
      ep = lCreateElem(CF_Type);
      lSetString(ep, CF_name, REPRIORITIZE);
      lAppendElem(entries, ep);
   }

   lSetString(ep, CF_value, ((true == aFlag) ? "1" : "0"));
   lSetUlong(ep, CF_local, 0);

   DRETURN_VOID;
} /* sge_set_conf_reprioritize */

/* 
 * Modify configuration with name 'aConfName'. 'anOldConf' is a *COPY* of this
 * configuration. 'aNewConf' is the new configuration.
 *
 * NOTE: Either 'anOldConf' or 'aNewConf' could be an empty configuration!
 * Empty configurations do not contain any 'CONF_entries'.
 */
static int do_mod_config(sge_gdi_ctx_class_t *ctx, char *aConfName, lListElem *anOldConf, lListElem *aNewConf, lList**anAnswer)
{
   lList *old_entries = NULL;
   lList *new_entries = NULL;
   lListElem* reprio = NULL;
   
   DENTER(TOP_LAYER, "do_mod_config");
   
   old_entries = lGetList(anOldConf, CONF_entries); 
   new_entries = lGetList(aNewConf, CONF_entries);
   
   if ((reprio = is_reprioritize_missing(old_entries, new_entries)) != NULL) {
      lAppendElem(new_entries, reprio);
   }

   if (check_static_conf_entries(old_entries, new_entries, anAnswer) != 0) {
      DRETURN(-1);
   }
      
   exchange_conf_by_name(ctx, aConfName, anOldConf, aNewConf, anAnswer);

   if (has_reschedule_unknown_change(old_entries, new_entries) == true) {
      update_reschedule_unknown_timout_values(aConfName);
   }
         
   DRETURN(0);
}

/*
 * Static configuration entries may be changed at runtime with a warning.
 */
static int check_static_conf_entries(lList *theOldConfEntries, lList *theNewConfEntries, lList **anAnswer)
{
   int entry_idx = 0;
   
   DENTER(TOP_LAYER, "check_static_conf_entries");

   while (NULL != Static_Conf_Entries[entry_idx]) {
      lListElem *old_entry, *new_entry = NULL;
      const char *old_value, *new_value = NULL;
      const char *entry_name = Static_Conf_Entries[entry_idx];
      
      old_entry = lGetElemStr(theOldConfEntries, CF_name, entry_name);
      new_entry = lGetElemStr(theNewConfEntries, CF_name, entry_name);
      
      if ((NULL != old_entry) && (NULL != new_entry)) {
         old_value = lGetString(old_entry, CF_value);
         new_value = lGetString(new_entry, CF_value);
         
         if (((NULL != old_value) && (NULL != new_value)) && (strcmp(old_value, new_value) != 0)) {
            /* log in qmaster messages file */
            WARNING((SGE_EVENT, MSG_WARN_CHANGENOTEFFECTEDUNTILRESTARTOFEXECHOSTS, entry_name));
         /*   INFO((SGE_EVENT, MSG_WARN_CHANGENOTEFFECTEDUNTILRESTARTOFEXECHOSTS, entry_name)); */
            answer_list_add(anAnswer, SGE_EVENT, STATUS_OK, ANSWER_QUALITY_INFO);
         }
      } else if ((NULL != old_entry) != (NULL != new_entry)) {
         /* log error only if one value is set */ 
         /* log in qmaster messages file */
         WARNING((SGE_EVENT, MSG_WARN_CHANGENOTEFFECTEDUNTILRESTARTOFEXECHOSTS, entry_name));
         /* INFO((SGE_EVENT, MSG_WARN_CHANGENOTEFFECTEDUNTILRESTARTOFEXECHOSTS, entry_name)); */
         answer_list_add(anAnswer, SGE_EVENT, STATUS_OK, ANSWER_QUALITY_INFO);
      }
      entry_idx++;
   }
   
   DRETURN(0);
}

/*
 * If 'theOldConfEntries' do contain a reprioritize element and 'theNewConfEntries'
 * do not, return a *COPY* of 'theNewConfEntries' reprioritize element.
 */
static lListElem* is_reprioritize_missing(lList *theOldConfEntries, lList *theNewConfEntries)
{
   lListElem *old_reprio = NULL;
   lListElem *new_reprio = NULL;
   lListElem *res = NULL;

   DENTER(TOP_LAYER, "is_reprioritize_missing");

   old_reprio = lGetElemStr(theOldConfEntries, CF_name, REPRIORITIZE);
   new_reprio = lGetElemStr(theNewConfEntries, CF_name, REPRIORITIZE);
   
   if ((NULL == new_reprio) && (NULL != old_reprio)) {
      res = lCopyElem(old_reprio);
   }
   
   DRETURN(res);
}

/*
 * Remove configuration 'aConfName' from cluster configuration and append 'aNewConf' to it. 
 *
 * NOTE: 'anOldConf' is a *COPY* of the old configuration entry.
 */
static int exchange_conf_by_name(sge_gdi_ctx_class_t *ctx, char *aConfName, lListElem *anOldConf, lListElem *aNewConf, lList**anAnswer)
{
   lListElem *elem = NULL;
   u_long32 old_version, new_version = 0;
   const char *old_conf_name = lGetHost(anOldConf, CONF_name);
   object_description *object_base = object_type_get_object_description();
   lList *config_list = *(object_base[SGE_TYPE_CONFIG].list);
   
   DENTER(TOP_LAYER, "exchange_conf_by_name");

   old_version = lGetUlong(anOldConf, CONF_version);
   
   new_version = (old_version + 1);
   
   lSetUlong(aNewConf, CONF_version, new_version); 
     
   /* Make sure, 'aNewConf' does have a unique name */
   lSetHost(aNewConf, CONF_name, old_conf_name);   

   SGE_LOCK(LOCK_MASTER_CONF, LOCK_WRITE);
   
   elem = lGetElemHost(config_list, CONF_name, old_conf_name);

   lRemoveElem(config_list, &elem);
   
   elem = lCopyElem(aNewConf);
   
   lAppendElem(config_list, elem);

   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_WRITE);

   sge_event_spool(ctx, anAnswer, 0, sgeE_CONFIG_MOD, 0, 0, old_conf_name, NULL, NULL, elem, NULL, NULL, true, true);
   
   DRETURN(0);
}

static bool has_reschedule_unknown_change(lList *theOldConfEntries, lList *theNewConfEntries)
{
   lListElem *old_elem = NULL;
   lListElem *new_elem = NULL;
   const char *old_value = NULL;
   const char *new_value = NULL;
   bool res = false;
   
   DENTER(TOP_LAYER, "has_reschedule_unknown_change");
   
   old_elem = lGetElemStr(theOldConfEntries, CF_name, "reschedule_unknown");
   new_elem = lGetElemStr(theNewConfEntries, CF_name, "reschedule_unknown");
   
   old_value = (NULL != old_elem) ? lGetString(old_elem, CF_value) : NULL;
   new_value = (NULL != new_elem) ? lGetString(new_elem, CF_value) : NULL;
      
   if ((NULL == old_value) || (NULL == new_value)) {
      res = true;  /* change by omission in one configuration */
   } else if (((NULL != old_value) && (NULL != new_value)) && (strcmp(old_value, new_value) != 0)) {
      res = true;  /* value did change */
   }
   
   if (true == res) {
      DPRINTF(("%s: reschedule_unknown did change!\n", SGE_FUNC));   
   }
   
   DRETURN(res);
}

static int do_add_config(sge_gdi_ctx_class_t *ctx, char *aConfName, lListElem *aConf, lList**anAnswer)
{
   lListElem *elem = NULL;
   object_description *object_base = object_type_get_object_description();
   lList *config_list = *(object_base[SGE_TYPE_CONFIG].list);

   DENTER(TOP_LAYER, "do_add_config");

   elem = lCopyElem(aConf);

   SGE_LOCK(LOCK_MASTER_CONF, LOCK_WRITE);
   
   lAppendElem(config_list, elem);

   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_WRITE);

   sge_event_spool(ctx, anAnswer, 0, sgeE_CONFIG_ADD, 0, 0, aConfName, NULL, NULL, elem, NULL, NULL, true, true);
   
   DRETURN(0);
}

static int remove_conf_by_name(char *aConfName)
{
   lListElem *elem = NULL;
   object_description *object_base = object_type_get_object_description();
   lList *config_list = *(object_base[SGE_TYPE_CONFIG].list);
   
   DENTER(TOP_LAYER, "remove_conf_by_name");

   SGE_LOCK(LOCK_MASTER_CONF, LOCK_WRITE);

   elem = lGetElemHost(config_list, CONF_name, aConfName);

   lRemoveElem(config_list, &elem);

   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_WRITE);
   
   DRETURN(0);
}
