/*
*         OpenPBS (Portable Batch System) v2.3 Software License
*
* Copyright (c) 1999-2000 Veridian Information Solutions, Inc.
* All rights reserved.
*
* ---------------------------------------------------------------------------
* For a license to use or redistribute the OpenPBS software under conditions
* other than those described below, or to purchase support for this software,
* please contact Veridian Systems, PBS Products Department ("Licensor") at:
*
*    www.OpenPBS.org  +1 650 967-4675                  sales@OpenPBS.org
*                        877 902-4PBS (US toll-free)
* ---------------------------------------------------------------------------
*
* This license covers use of the OpenPBS v2.3 software (the "Software") at
* your site or location, and, for certain users, redistribution of the
* Software to other sites and locations.  Use and redistribution of
* OpenPBS v2.3 in source and binary forms, with or without modification,
* are permitted provided that all of the following conditions are met.
* After December 31, 2001, only conditions 3-6 must be met:
*
* 1. Commercial and/or non-commercial use of the Software is permitted
*    provided a current software registration is on file at www.OpenPBS.org.
*    If use of this software contributes to a publication, product, or
*    service, proper attribution must be given; see www.OpenPBS.org/credit.html
*
* 2. Redistribution in any form is only permitted for non-commercial,
*    non-profit purposes.  There can be no charge for the Software or any
*    software incorporating the Software.  Further, there can be no
*    expectation of revenue generated as a consequence of redistributing
*    the Software.
*
* 3. Any Redistribution of source code must retain the above copyright notice
*    and the acknowledgment contained in paragraph 6, this list of conditions
*    and the disclaimer contained in paragraph 7.
*
* 4. Any Redistribution in binary form must reproduce the above copyright
*    notice and the acknowledgment contained in paragraph 6, this list of
*    conditions and the disclaimer contained in paragraph 7 in the
*    documentation and/or other materials provided with the distribution.
*
* 5. Redistributions in any form must be accompanied by information on how to
*    obtain complete source code for the OpenPBS software and any
*    modifications and/or additions to the OpenPBS software.  The source code
*    must either be included in the distribution or be available for no more
*    than the cost of distribution plus a nominal fee, and all modifications
*    and additions to the Software must be freely redistributable by any party
*    (including Licensor) without restriction.
*
* 6. All advertising materials mentioning features or use of the Software must
*    display the following acknowledgment:
*
*     "This product includes software developed by NASA Ames Research Center,
*     Lawrence Livermore National Laboratory, and Veridian Information
*     Solutions, Inc.
*     Visit www.OpenPBS.org for OpenPBS software support,
*     products, and information."
*
* 7. DISCLAIMER OF WARRANTY
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT
* ARE EXPRESSLY DISCLAIMED.
*
* IN NO EVENT SHALL VERIDIAN CORPORATION, ITS AFFILIATED COMPANIES, OR THE
* U.S. GOVERNMENT OR ANY OF ITS AGENCIES BE LIABLE FOR ANY DIRECT OR INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This license will be governed by the laws of the Commonwealth of Virginia,
* without reference to its choice of law rules.
*/
/*
 * svr_manager.c
 *
 * Functions relating to the Manager Batch Request (qmgr).
 *
 * Included funtions are:
 *
 * mgr_set_attr()  - set attributes for an server/queue object
 * mgr_set_node_attr() - sets "node-attributes" (state,properties etc)
 *      on a node
 * mgr_queue_create() - create a queue
 * mgr_queue_delete() - delete a queue
 * mgr_server_set() - set server attributes
 * mgr_server_unset() - unset server attributes
 * mgr_queue_set()  - set queue attributes
 * mgr_queue_unset() - unset queue attributes
 * mgr_node_set()  - top level for setting "node-attributes" on
 *      a set of nodes
 *      mgr_node_delete()       - top level function for effectively deleting a node
 *      mgr_node_create()       - top level function for creating a node
 * req_manager()  - process manager request (top level)
 * disallowed_types_chk() - at action for queue disallowed_types pbs_attribute
 */
#include <pbs_config.h>   /* the master config generated by configure */
#include "req_manager.h"

#include <sys/types.h>
#include "libpbs.h"
#include <ctype.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include "server_limits.h"
#include "list_link.h"
#include "attribute.h"
#include "resource.h"
#include "server.h"
#include "queue.h"
#include "credential.h"
#include "utils.h"
#include "batch_request.h"
#include "net_connect.h"
#include "pbs_error.h"
#include "log.h"
#include "../lib/Liblog/pbs_log.h"
#include "../lib/Liblog/log_event.h"
#include "svrfunc.h"
#include "pbs_job.h"
#include "pbs_nodes.h"
#include "work_task.h"
#include "mcom.h"
#include "../lib/Libattr/attr_node_func.h" /* free_prop_list */
#include "node_func.h" /* init_prop, find_nodebyname, reinitialize_node_iterator, recompute_ntype_cnts, effective_node_delete, create_pbs_node */
#include "node_manager.h" /* setup_notification */
#include "../lib/Libutils/u_lock_ctl.h" /* unlock_node */
#include "queue_func.h" /* find_queuebyname, que_alloc, que_free */
#include "queue_recov.h" /* que_save */
#include "mutex_mgr.hpp"


#define PERM_MANAGER (ATR_DFLAG_MGWR | ATR_DFLAG_MGRD)
#define PERM_OPorMGR (ATR_DFLAG_MGWR | ATR_DFLAG_MGRD | ATR_DFLAG_OPRD | ATR_DFLAG_OPWR)

/* Global Data Items: */

extern int            LOGLEVEL;
extern struct server  server;
extern attribute_def  que_attr_def[];
extern attribute_def  svr_attr_def[];
extern attribute_def  node_attr_def[];   /* node attributes defs */
extern int            svr_chngNodesfile;
extern char          *msg_attrtype;
extern char          *msg_daemonname;
extern char          *msg_manager;
extern char          *msg_man_cre;
extern char          *msg_man_del;
extern char          *msg_man_set;
extern char          *msg_man_uns;
extern int            disable_timeout_check;


extern int que_purge(pbs_queue *);
extern void save_characteristic(struct pbsnode *, node_check_info *);
extern int chk_characteristic(struct pbsnode *, node_check_info *, int *);
extern int hasprop(struct pbsnode *, struct prop *);
extern int PNodeStateToString(int, char *, int);



/* private data */

static const char *all_quename = "_All_";
static const char *all_nodes_str = "_All_";

/*
 * check_que_attr - check if attributes in request are consistent with
 * the current queue type.  This is called when creating or setting
 * the attributes of a queue.
 *
 * Returns: NULL if all ok or name of bad pbs_attribute if not ok
 */

static const char *check_que_attr(
    
  pbs_queue *pque)

  {
  int   i;
  int   type;

  type = pque->qu_qs.qu_type;  /* current type of queue */

  for (i = 0; i < QA_ATR_LAST; ++i)
    {
    if (pque->qu_attr[i].at_flags & ATR_VFLAG_SET)
      {
      if (que_attr_def[i].at_parent == PARENT_TYPE_QUE_EXC)
        {
        if (type == QTYPE_Unset)
          type = QTYPE_Execution;
        else if (type != QTYPE_Execution)
          return (que_attr_def[i].at_name);

        }
      else if (que_attr_def[i].at_parent == PARENT_TYPE_QUE_RTE)
        {
        if (type == QTYPE_Unset)
          type = QTYPE_RoutePush;
        else if (type != QTYPE_RoutePush)
          return (que_attr_def[i].at_name);
        }
      }
    }

  return ((char *)0); /* all attributes are ok */
  } /* END check_que_attr() */




/*
 * check_que_enable - check if attempt to enable incompletely defined queue
 * This is the at_action() routine for QA_ATR_Enabled
 */

int check_que_enable(

  pbs_attribute *pattr,
  void          *pque, /* actually a queue* */
  int            mode)

  {
  pbs_attribute *datr;

  if (pattr->at_val.at_long != 0)
    {

    /*
     * admin attempting to  enabled queue,
     * is it completely defined
     */

    if (((pbs_queue *)pque)->qu_qs.qu_type == QTYPE_Unset)
      return (PBSE_QUENOEN);
    else if (((pbs_queue *)pque)->qu_qs.qu_type == QTYPE_RoutePush)
      {
      datr = &((pbs_queue *)pque)->qu_attr[QR_ATR_RouteDestin];

      if (!(datr->at_flags & ATR_VFLAG_SET) ||
          (datr->at_val.at_arst->as_usedptr == 0))
        return (PBSE_QUENOEN);
      }
    }

  return(PBSE_NONE); /* ok to enable queue */
  } /* END check_que_enable() */




/*
 * set_queue_type - check the requested value of the queue type pbs_attribute
 * and set qu_type accordingly.
 * This is the at_action() routine for QA_ATR_QType
 */

int set_queue_type(
    
  pbs_attribute *pattr,
  void          *pque,
  int            mode)

  {
  int   i;
  char *pca;
  const char *pcv;
  int   spectype;

  static struct
    {
    int   type;
    const char *name;
    } qt[2] =

    {
      { QTYPE_Execution, "Execution" },
    { QTYPE_RoutePush, "Route" }
    };

  if ((pattr->at_flags & ATR_VFLAG_SET) == 0)
    /* better be set or we shouldn't be here */
    return (PBSE_BADATVAL);

  /* does the requested value match a legal value? */

  for (i = 0; i < 2; i++)
    {
    spectype = qt[i].type;
    pca = pattr->at_val.at_str;
    pcv = qt[i].name;

    if (*pca == '\0')
      return (PBSE_BADATVAL);

    while (*pca)
      {
      if (toupper((int)*pca++) != toupper((int)*pcv++))
        {
        spectype = -1; /* no match */
        break;
        }
      }

    if (spectype != -1)   /* set up the pbs_attribute */
      {
      ((pbs_queue *)pque)->qu_qs.qu_type = spectype;
      free(pattr->at_val.at_str);
      pattr->at_val.at_str = (char *)calloc(1, strlen(qt[i].name) + 1);

      if (pattr->at_val.at_str == (char *)0)
        return (PBSE_SYSTEM);

      strcpy(pattr->at_val.at_str, qt[i].name);

      return(PBSE_NONE);
      }
    }

  return (PBSE_BADATVAL);
  } /* set_queue_type() */




/*
 * mgr_log_attr - log the change of an pbs_attribute
 */

static void mgr_log_attr(

  char *msg,
  struct svrattrl *plist,
  int logclass,           /* see log.h */
  char *objname)          /* object being modified */

  {
  const char *pstr;
  char  log_buf[LOCAL_LOG_BUF_SIZE];

  while (plist)
    {
    snprintf(log_buf, sizeof(log_buf), "%s%s", msg, plist->al_name);

    if (plist->al_rescln)
      {
      int len = strlen(log_buf);
      snprintf(log_buf + len, sizeof(log_buf) - len, ".%s", plist->al_resc);
      }

    if (plist->al_op == INCR)
      pstr = " + ";
    else if (plist->al_op == DECR)
      pstr = " - ";
    else
      pstr = " = ";

    safe_strncat(log_buf, pstr, sizeof(log_buf) - strlen(log_buf) - 1);

    if (plist->al_valln)
      safe_strncat(log_buf, plist->al_value, sizeof(log_buf) - strlen(log_buf) - 1);

    log_buf[LOCAL_LOG_BUF_SIZE-1] = '\0';

    log_event(PBSEVENT_ADMIN, logclass, objname, log_buf);

    plist = (struct svrattrl *)GET_NEXT(plist->al_link);
    }
  } /* END mgr_log_attr() */




/*
 * mgr_set_attr - set attributes for manager function
 *
 * The set operation is performed as an atomic operation: all specified
 * attributes must be successfully set, or none are modified.
 */

static int mgr_set_attr(

  pbs_attribute *pattr,  /* current attributes */
  attribute_def *pdef,
  int            limit,
  svrattrl      *plist,
  int            privil,
  int           *bad,
  void          *parent,
  int            mode)

  {
  int            index;
  pbs_attribute *new_attr;
  pbs_attribute *pnew;
  pbs_attribute *pold;
  int            rc;

  if (plist == (struct svrattrl *)0)
    {
    return(0);
    }

  new_attr = (pbs_attribute *)calloc((unsigned int)limit, sizeof(pbs_attribute));

  if (new_attr == NULL)
    {
    return(PBSE_SYSTEM);
    }

  /*
   * decode the new_attr pbs_attribute values which are in the request,
   * copy the corresponding current pbs_attribute into a holding array
   * and update it with the newly decoded value
   */

  if ((rc = attr_atomic_set(plist, pattr, new_attr, pdef, limit, -1, privil, bad)) != 0)
    {
    attr_atomic_kill(new_attr, pdef, limit);

    return(rc);
    }

  for (index = 0;index < limit;index++)
    {
    pnew = new_attr + index;

    pold = pattr + index;

    if (pnew->at_flags & ATR_VFLAG_MODIFY)
      {
      /*
       * for each pbs_attribute which is to be modified, call the
       * at_action routine for the pbs_attribute, if one exists, with the
       *  new value.  If the action fails, undo everything.
       */

      if ((pdef + index)->at_action)
        {
        if ((rc = (pdef + index)->at_action(new_attr + index, parent, mode)))
          {
          /* always allow removing from ACLs */
          if (!((plist->al_op == DECR) && (pdef + index)->at_type == ATR_TYPE_ACL))
            {
            attr_atomic_kill(new_attr, pdef, limit);

            return(rc);
            }
          }
        }

      if ((index == SRV_ATR_tcp_timeout) &&
          (pnew->at_val.at_long < 300))
        disable_timeout_check = TRUE;

      /* now replace the old values with any modified new values */

      (pdef + index)->at_free(pold);

      if ((pold->at_type == ATR_TYPE_LIST) ||
          (pold->at_type == ATR_TYPE_RESC))
        {
        list_move(&pnew->at_val.at_list, &pold->at_val.at_list);
        }
      else
        {
        *pold = *pnew;
        }

      pold->at_flags = pnew->at_flags; /* includes MODIFY */
      }
    }

  /*
   * we have moved all the "external" values to the old array, thus
   * we just free the new array, NOT call at_free on each.
   */

  free((char *)new_attr);

  return(0);
  }  /* END mgr_set_attr() */





/*
 * mgr_unset_attr - Unset (clear) attributes for manager function
 *
 * The set operation is performed as an atomic operation: all specified
 * attributes must be successfully set, or none are modified.
 */

int mgr_unset_attr(

  pbs_attribute *pattr,  /* current attributes */
  attribute_def *pdef,
  int            limit,
  svrattrl      *plist,
  int            privil,
  int           *bad)

  {
  int   index;
  int   ord;
  svrattrl *pl;
  resource_def *prsdef;
  resource *presc;

  /* first check the pbs_attribute exists and we have privilege to set */

  ord = 0;
  pl = plist;

  while (pl != NULL)
    {
    ord++;

    index = find_attr(pdef, pl->al_name, limit);

    if (index < 0)
      {
      *bad = ord;

      return(PBSE_NOATTR);
      }

    /* have we privilege to unset the pbs_attribute ? */

    if (((pdef + index)->at_flags & privil & ATR_DFLAG_WRACC) == 0)
      {
      *bad = ord;

      return(PBSE_ATTRRO);
      }

    if (((pdef + index)->at_type == ATR_TYPE_RESC) &&
        (pl->al_resc != NULL))
      {
      /* check the individual resource */

      prsdef = find_resc_def(
                 svr_resc_def,
                 pl->al_resc,
                 svr_resc_size);

      if (prsdef == NULL)
        {
        *bad = ord;

        return(PBSE_UNKRESC);
        }

      if ((prsdef->rs_flags & privil & ATR_DFLAG_WRACC) == 0)
        {
        *bad = ord;

        return(PBSE_PERM);
        }
      }

    pl = (svrattrl *)GET_NEXT(pl->al_link);
    }

  /* ok, now clear them */

  while (plist != NULL)
    {
    index = find_attr(pdef, plist->al_name, limit);

    if (((pdef + index)->at_type == ATR_TYPE_RESC) &&
        (plist->al_resc != NULL))
      {
      /* free resource member, not the pbs_attribute */

      prsdef = find_resc_def(
                 svr_resc_def,
                 plist->al_resc,
                 svr_resc_size);

      if ((presc = find_resc_entry(pattr + index, prsdef)))
        {
        if ((pdef->at_parent != PARENT_TYPE_SERVER) ||
            (index != SRV_ATR_resource_cost))
          {
          prsdef->rs_free(&presc->rs_value);
          }

        delete_link(&presc->rs_link);
        }

      free(presc);
      }
    else
      {
      /* free the whole pbs_attribute */

      (pdef + index)->at_free(pattr + index);

      (pattr + index)->at_flags |= ATR_VFLAG_MODIFY;
      }

    plist = (svrattrl *)GET_NEXT(plist->al_link);
    }  /* END while (plist != NULL) */

  return(0);
  }  /* END mgr_unset_attr() */





/*
 * update_subnode - update subnodes with state and props
 *
 * state information comes from the parent node
 * prop information taken from the 1st subnode
 *
 * Note, while it isn't a problem now, if the server is ever multithreaded,
 * this will have to locked out while subnodes are being allocated or
 * otherwise updated.
 */

void update_subnode(

  struct pbsnode *pnode)

  {

/*  unsigned short   state;

  state = pnode->nd_state & INUSE_COMMON_MASK;

  for (pnxtsn = pnode->nd_psn;pnxtsn;pnxtsn = pnxtsn->next)
    {
    pnxtsn->host  = pnode;

    pnxtsn->inuse = (pnxtsn->inuse & ~INUSE_COMMON_MASK) | state;
    } */

  return;
  }  /* END update_subnode() */






/*
 * mgr_set_node_attr - "set state, properties, ntype" for mgr_node_set function
 *
 * The set operation is performed as an atomic operation--  all specified
 * items must be successfully set for the node, or none are modified.
 *
 * Although a node doesn't, at present, have an array of attributes as one
 * of its structure members, we will proceed as though it did by using the
 *      node to derive a temporary array of attributes.  We use the "action"
 *      function of our artificial attribute_def array with a mode value of NEW
 *      in constructing this temporary node pbs_attribute array. Once this temporary
 *      array is constucted, the "svrattrl" request list is processed against it
 * to produce a new_attr "updated" pbs_attribute array.  When all the pbs_attribute
 *      processing completes, assuming it's successful, the updated pbs_attribute
 *      array is used to modify the node itself-- this can be done via each
 *      pbs_attribute's "action" function with the value of mode set to "ALTER".
 * Although it's a little twisted to use the idea of an pbs_attribute's
 * "action function" to move between node an pbs_attribute,it pushes the
 *      details down a level, below that which is taking place here-- a good
 *      thing.
 */

int mgr_set_node_attr(

  struct pbsnode *pnode,  /* do some kind of "set" action on this node */
  attribute_def  *pdef,   /* pbsnode doesn't have attributes but in the
                             future it might  so, make  code similar to existing */
  int             limit,  /* length of the *pdef array      */
  svrattrl       *plist,  /* batch request's svrattrl list begins here */
  int             privil, /* requester's privilege          */
  int            *bad,    /* if there is a "bad pbs_attribute" pass back 
                             position via this loc */
  void           *parent, /*may go unused in this function */
  int             mode)  /*passed to attrib's action func not used by 
                           this func at this time*/

  {
  int              i;
  int              index;
  int              nstatus = 0;
  int              nprops = 0;
  int              rc;
  pbs_attribute   *new_attr;
  pbs_attribute   *unused = NULL;
  pbs_attribute   *pnew;

  struct pbsnode   tnode;  /*temporary node*/

  struct prop     *pdest;

  struct prop    **plink;
  char             log_buf[LOCAL_LOG_BUF_SIZE];

  if (plist == NULL)
    {
    return(0);  /* nothing to do, return success */
    }

  /* Get heap space for a temporary node-pbs_attribute array and use the
   * various "node-attribute action" functions defined in the file
   * "node_attr_def.c" to set the current values of each entry's
   * structure members
   */

  new_attr = (pbs_attribute *)calloc((unsigned int)limit, sizeof(pbs_attribute));

  if (new_attr == NULL)
    {
    return(PBSE_SYSTEM);
    }

  for (index = 0; index < limit; index++)
    {
    if ((pdef + index)->at_action)
      {
      if ((rc = (pdef + index)->at_action(new_attr + index, (void *)pnode, ATR_ACTION_NEW)))
        {
        /*call the "at_free" func for each member of*/
        /*new" and then free the entire "new" array */
        /*return code (rc) shapes caller's reply    */

        attr_atomic_kill(new_attr, pdef, limit);

        return(rc);
        }
      }
    }

  /*
   * The function "attr_atomic_node_set" does the following:
   * successively decodes the new pbs_attribute modifications carried in the
   * the request list, "plist", into a temporary pbs_attribute (on stack).
   * Updates each pbs_attribute in the derived node-attribute array using the
   * decoded values.  If a failure of some sort occurs, as evidenced by
   * a non-zero return code (rc), call attr_atomic_kill() to undo
   * everything-- calls upon the "at_free" function for each pbs_attribute
   * to free hanging structures, then frees up the "new" array
   * return code (rc) shapes caller's reply
   */

  if ((rc = attr_atomic_node_set(plist, unused, new_attr, pdef, limit, -1, privil, bad)) != 0)
    {
    attr_atomic_kill(new_attr, pdef, limit);

    return(rc);
    }

  /*
   * Use the updated pbs_attribute values in pbs_attribute array, "new", to update a
   * temporary (on stack) pbsnode, which is a copy of the one pointed to by
   * pnode.  If all goes well, we are home free-- just copy the information
   * in the temporary pbsnode, after it has gotten modified, back to the
   * node pointed to by "pnode" and free the pbs_attribute array "new".
   */

  tnode = *pnode;

  for (index = 0;index < limit;index++)
    {
    pnew = new_attr + index;

    if (pnew->at_flags & ATR_VFLAG_MODIFY)
      {
      /*
       * for each pbs_attribute which is to cause modification to the node, "tnode",
       * call the specified "at_action" routine for that pbs_attribute passing it
       * the address of the temporary pbs_attribute and the address of the temporary
       * pbsnode object.  If any of the update actions fail along the way, undo
       * everything.   Any calloc'd substructures should be hanging off the
       * appropriate pbs_attribute of the pbs_attribute array "new".
       */

      if ((pdef + index)->at_action)
        {
        if ((rc = (pdef + index)->at_action(new_attr + index, (void *)&tnode, ATR_ACTION_ALTER)))
          {
          attr_atomic_kill(new_attr, pdef, limit);

          return(rc);
          }
        }
      }
    }

  /*
   * we have successfully updated tnode; use it to modify pnode
   * if pnode has any calloc-ed storage that is being replaced
   * be sure to free the old.
   */

  if (pnode->nd_prop && (pnode->nd_prop != tnode.nd_prop))
    {
    if (pnode->nd_prop->as_buf)
      free(pnode->nd_prop->as_buf);

    free(pnode->nd_prop);

    pnode->nd_prop = NULL;
    }

  /* NOTE:  nd_status properly freed during pbs_attribute alter */

  if ((pnode->nd_state != tnode.nd_state))
    {
    char OrigState[1024];
    char FinalState[1024];

    /* changing node state */

    /* log change */

    PNodeStateToString(pnode->nd_state, OrigState, sizeof(OrigState));
    PNodeStateToString(tnode.nd_state, FinalState, sizeof(FinalState));

    sprintf(log_buf, "node %s state changed from %s to %s",
      pnode->nd_name,
      OrigState,
      FinalState);

    log_event(PBSEVENT_ADMIN,PBS_EVENTCLASS_NODE,pnode->nd_name,log_buf);
    }

  /* NOTE:  nd_status properly freed during pbs_attribute alter */

  /*
    if ((pnode->nd_status != NULL) && (pnode->nd_status != tnode.nd_status))
      {
      if (pnode->nd_status->as_buf != NULL)
        free(pnode->nd_status->as_buf);

      free(pnode->nd_status);

      pnode->nd_status = NULL;
      }
  */

  *pnode              = tnode;        /* updates all data including linking in props */

  free(new_attr);  /*any new  prop list has been put on pnode*/

  /*dispense with the pbs_attribute array itself*/

  /* update prop list based on new prop array */

  free_prop_list(pnode->nd_first);

  plink = &pnode->nd_first;

  if (pnode->nd_prop)
    {
    nprops = pnode->nd_prop->as_usedptr;

    for (i = 0;i < nprops;++i)
      {
      pdest = init_prop(pnode->nd_prop->as_string[i]);

      *plink = pdest;
      plink  = &pdest->next;
      }
    }

  /* now add in name as last prop */

  pdest  = init_prop(pnode->nd_name);

  *plink = pdest;

  pnode->nd_last = pdest;

  pnode->nd_nprops = nprops + 1;

  /* update status list based on new status array */

  free_prop_list(pnode->nd_f_st);

  plink = &pnode->nd_f_st;

  if (pnode->nd_status != NULL)
    {
    nstatus = pnode->nd_status->as_usedptr;

    for (i = 0;i < nstatus;++i)
      {
      pdest = init_prop(pnode->nd_status->as_string[i]);

      *plink = pdest;
      plink  = &pdest->next;
      }
    }

  /* now add in name as last status */

  pdest  = init_prop(pnode->nd_name);

  *plink = pdest;

  pnode->nd_l_st = pdest;

  pnode->nd_nstatus = nstatus + 1;

  /* now update subnodes */

  update_subnode(pnode);

  return(0);
  }  /* END mgr_set_node_attr() */




/*
 * mgr_queue_create - process request to create a queue
 *
 * Creates queue and calls mgr_set_attr to set queue attributes.
 */

void mgr_queue_create(

  struct batch_request *preq)

  {
  int        bad;
  const char      *badattr;
  svrattrl  *plist;
  pbs_queue *pque;
  int        rc;
  char       log_buf[LOCAL_LOG_BUF_SIZE];

  rc = strlen(preq->rq_ind.rq_manager.rq_objname);

  if ((rc > PBS_MAXQUEUENAME) || (rc == 0))
    {
    req_reject(PBSE_QUENBIG, 0, preq, NULL, NULL);

    return;
    }

  if ((pque = find_queuebyname(preq->rq_ind.rq_manager.rq_objname)))
    {
    unlock_queue(pque, "mgr_queue_create", (char *)"fail", LOGLEVEL);
    req_reject(PBSE_QUEEXIST, 0, preq, NULL, NULL);
    return;
    }

  pque = que_alloc(preq->rq_ind.rq_manager.rq_objname, FALSE);

  mutex_mgr que_mgr(pque->qu_mutex, true);

  /* set the queue attributes */
  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  rc = mgr_set_attr(
         pque->qu_attr,
         que_attr_def,
         QA_ATR_LAST,
         plist,
         preq->rq_perm,
         &bad,
         (void *)pque,
         ATR_ACTION_NEW);

  if (rc != 0)
    {
    reply_badattr(rc, bad, plist, preq);

    que_free(pque, FALSE);
    que_mgr.set_unlock_on_exit(false);
    }
  else
    {
    que_save(pque);
    svr_save(&server, SVR_SAVE_QUICK);

    sprintf(log_buf, msg_manager,
            msg_man_cre,
            preq->rq_user,
            preq->rq_host);

    log_event(PBSEVENT_ADMIN,PBS_EVENTCLASS_QUEUE,pque->qu_qs.qu_name,log_buf);

    mgr_log_attr(
      msg_man_set,
      plist,
      PBS_EVENTCLASS_QUEUE,
      preq->rq_ind.rq_manager.rq_objname);

    /* check the appropriateness of the attributes vs. queue type */
    if ((badattr = check_que_attr(pque)) != NULL)
      {
      /* mismatch, issue warning */
      sprintf(log_buf, msg_attrtype, pque->qu_qs.qu_name, badattr);

      reply_text(preq, PBSE_ATTRTYPE, log_buf);
      }
    else
      {
      reply_ack(preq);
      }
    }

  return;
  } /* END mgr_queue_create() */






/*
 * mgr_queue_delete - delete a queue
 *
 * The queue must be empty of jobs
 */

void mgr_queue_delete(

  struct batch_request *preq)

  {
  pbs_queue *pque;
  int        rc;
  char       log_buf[LOCAL_LOG_BUF_SIZE];

  pque = find_queuebyname(preq->rq_ind.rq_manager.rq_objname);

  if (pque == NULL)
    {
    /* FAILURE */
    req_reject(PBSE_UNKQUE, 0, preq, NULL, NULL);

    return;
    }
  mutex_mgr pque_mutex(pque->qu_mutex, true);

  if ((rc = que_purge(pque)) != PBSE_NONE)
    {
    /* FAILURE */
    pque_mutex.unlock();
    req_reject(rc, 0, preq, NULL, NULL);
    return;
    }
  pque_mutex.set_unlock_on_exit(false);

  svr_save(&server, SVR_SAVE_QUICK);

  sprintf(log_buf, msg_manager, msg_man_del, preq->rq_user, preq->rq_host);

  log_event(
    PBSEVENT_ADMIN,
    PBS_EVENTCLASS_QUEUE,
    preq->rq_ind.rq_manager.rq_objname,
    log_buf);

  reply_ack(preq);

  /* SUCCESS */

  return;
  }  /* END mgr_queue_delete() */




int hostname_check(

  char *hostname)

  {
  char  myhost[PBS_MAXHOSTNAME + 1];
  char  extension[PBS_MAXHOSTNAME + 1];
  char  ret_hostname[PBS_MAXHOSTNAME + 1];
  char *open_bracket;

  if (hostname == NULL)
    return(SUCCESS);
  else if (strchr(hostname,'*') != NULL)
    return(SUCCESS);

  snprintf(myhost, sizeof(myhost), "%s", hostname);
  open_bracket = strchr(myhost,'[');

  ret_hostname[0] = '\0';

  if (open_bracket == NULL)
    {
    /* handle normally */
    if ((get_fullhostname(hostname,ret_hostname,PBS_MAXHOSTNAME,NULL)) ||
        strncasecmp(hostname, ret_hostname, PBS_MAXHOSTNAME))
      {
      return(FAILURE);
      }
    }
  else
    {
    /* authenticate the range */
    int   low;
    int   high;
    char *dash;
    char *host_end;
    char *close_bracket = strchr(open_bracket,']');

    low = atoi(open_bracket+1);
    dash = strchr(open_bracket,'-');

    host_end = open_bracket + strlen(open_bracket);

    if ((dash == NULL) ||
        (close_bracket == NULL))
      {
      /* not a valid range */
      return(FAILURE);
      }

    high = atoi(dash+1);

    /* check for anything left over */
    extension[0] = '\0';

    if (close_bracket < host_end)
      {
      snprintf(extension, sizeof(extension), "%s", close_bracket+1);
      }

    /* now check each hostname */
    while (low < high)
      {
      sprintf(open_bracket,"%d%s",low,extension);

      if ((get_fullhostname(myhost,ret_hostname,PBS_MAXHOSTNAME,NULL)) ||
          (strncmp(myhost,ret_hostname,PBS_MAXHOSTNAME)))
        {
        return(FAILURE);
        }

      low++;
      }
    }

  return(SUCCESS);
  } /* END hostname_check() */





/*
 * mgr_server_set - Set Server Attribute Values
 *
 * Sets the requested attributes and returns a reply
 */

void mgr_server_set(

  struct batch_request *preq) /* I */

  {
  int       bad_attr = 0;
  svrattrl *plist;
  int       rc;
  char      log_buf[LOCAL_LOG_BUF_SIZE];

  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  pthread_mutex_lock(server.sv_attr_mutex);
  rc = mgr_set_attr(
         server.sv_attr,
         svr_attr_def,
         SRV_ATR_LAST,
         plist,
         preq->rq_perm,
         &bad_attr,
         (void *) & server,
         ATR_ACTION_ALTER);
  pthread_mutex_unlock(server.sv_attr_mutex);

  /* PBSE_BADACLHOST - lets show the user the first bad host in the ACL  */

  if (rc == PBSE_BADACLHOST)
    {
    char          *bad_host;
    char          *host_entry;
    pbs_attribute  temp;
    int            index;
    int            i;

    struct    array_strings *pstr;
    int       bhstrlen;

    bhstrlen = PBS_MAXHOSTNAME + 17;
    bad_host = (char *)calloc(sizeof(char), (bhstrlen + 1));

    if (bad_host == NULL)
      {
      reply_badattr(PBSE_BADHOST, bad_attr, plist, preq);

      return;
      }

    /* look into plist to find an offending attr */

    while (plist != NULL)
      {
      /* only concerned about operators and managers */

      if (strcmp(plist->al_name, ATTR_managers) && strcmp(plist->al_name, ATTR_operators))
        {
        plist = (svrattrl *)GET_NEXT(plist->al_link);

        continue;
        }

      if ((index = find_attr(svr_attr_def, plist->al_name, SRV_ATR_LAST)) < 0)
        {
        plist = (svrattrl *)GET_NEXT(plist->al_link);
        continue;
        }

      clear_attr(&temp, &svr_attr_def[index]);
      svr_attr_def[index].at_decode(&temp, plist->al_name, plist->al_resc, plist->al_value, 0);

      pstr = temp.at_val.at_arst;

      bad_host[0] = '\0';

      /* loop over all hosts in the request and perform same
         check as manager_oper_chk*/

      for (i = 0;i < pstr->as_usedptr;++i)
        {
        host_entry = strchr(pstr->as_string[i], (int)'@');
        
        if (hostname_check(host_entry+1) == FAILURE)
          {
          snprintf(bad_host, bhstrlen, "First bad host: %s",
            host_entry + 1);
        
          break;
          }
        }

      if (bad_host[0] != '\0')
        break;

      /* nothing wrong found in the request, let's try again
         with the server's list */

      pthread_mutex_lock(server.sv_attr_mutex);
      pstr = server.sv_attr[index].at_val.at_arst;

      for (i = 0; i < pstr->as_usedptr; ++i)
        {
        host_entry = strchr(pstr->as_string[i], (int)'@');

        if (hostname_check(host_entry+1) == FAILURE)
          {
          snprintf(bad_host,bhstrlen,"First bad host: %s",
            host_entry+1);

          break;
          }
        }
      pthread_mutex_unlock(server.sv_attr_mutex);

      if (bad_host[0] != '\0')
        break;

      plist = (svrattrl *)GET_NEXT(plist->al_link);
      }

    if (bad_host[0] != '\0') /* we found a fully qualified host that was bad */
      {
      req_reject(PBSE_BADACLHOST, 0, preq, NULL, bad_host);
      }
    else
      {
      /* this shouldn't happen (return PBSE_BADACLHOST, but now we can't
         find the bad host) */

      reply_badattr(PBSE_BADHOST, bad_attr, plist, preq);
      }

    free(bad_host);

    return;
    } /* END if (rc == PBSE_BADACLHOST) */
  else if (rc != 0)
    {
    reply_badattr(rc, bad_attr, plist, preq);

    return;
    }

  svr_save(&server, SVR_SAVE_FULL);

  sprintf(log_buf, msg_manager, msg_man_set, preq->rq_user, preq->rq_host);

  log_event(
    PBSEVENT_ADMIN,
    PBS_EVENTCLASS_SERVER,
    msg_daemonname,
    log_buf);

  mgr_log_attr(msg_man_set, plist, PBS_EVENTCLASS_SERVER, msg_daemonname);

  reply_ack(preq);

  return;
  }  /* END mgr_server_set() */






/*
 * mgr_server_unset - Unset (clear) Server Attribute Values
 *
 * Clears the requested attributes and returns a reply
 */

void mgr_server_unset(

  struct batch_request *preq)

  {
  int       bad_attr = 0;
  svrattrl *plist;
  int       rc;
  char      log_buf[LOCAL_LOG_BUF_SIZE];

  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  pthread_mutex_lock(server.sv_attr_mutex);
  rc = mgr_unset_attr(
         server.sv_attr,
         svr_attr_def,
         SRV_ATR_LAST,
         plist,
         preq->rq_perm,
         &bad_attr);
  pthread_mutex_unlock(server.sv_attr_mutex);

  if (rc != 0)
    {
    reply_badattr(rc, bad_attr, plist, preq);
    }
  else
    {
    svr_save(&server, SVR_SAVE_FULL);

    sprintf(log_buf, msg_manager, msg_man_uns, preq->rq_user, preq->rq_host);

    log_event(PBSEVENT_ADMIN,PBS_EVENTCLASS_SERVER,msg_daemonname,log_buf);

    mgr_log_attr(msg_man_uns, plist, PBS_EVENTCLASS_SERVER, msg_daemonname);

    reply_ack(preq);
    }

  return;
  }  /* END mgr_server_unset() */





/*
 * mgr_queue_set - Set Queue Attribute Values
 *
 * Finds the queue, Sets the requested attributes and returns a reply
 */

void mgr_queue_set(

  struct batch_request *preq)

  {
  int        allques;
  int        bad = 0;
  const char  *badattr;
  svrattrl  *plist;
  pbs_queue *pque;
  const char      *qname;
  int        rc;
  int        iter = -1;
  char       log_buf[LOCAL_LOG_BUF_SIZE];

  if (LOGLEVEL >= 7)
    {
    sprintf(log_buf, "from user %s", preq->rq_user);
    log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_QUEUE, __func__, log_buf);
    }

  if ((*preq->rq_ind.rq_manager.rq_objname == '\0') ||
      (*preq->rq_ind.rq_manager.rq_objname == '@'))
    {
    qname   = all_quename;
    allques = TRUE;
    pque = next_queue(&svr_queues,&iter);
    }
  else
    {
    qname   = preq->rq_ind.rq_manager.rq_objname;
    allques = FALSE;

    pque = find_queuebyname(qname);
    }

  if (pque == NULL)
    {
    req_reject(PBSE_UNKQUE, 0, preq, NULL, NULL);

    return;
    }
  /* set the attributes */
  sprintf(log_buf, msg_manager, msg_man_set, preq->rq_user, preq->rq_host);

  log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_QUEUE, qname, log_buf);

  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  while (pque != NULL)
    {
      {
      mutex_mgr que_mutex(pque->qu_mutex, true);

      rc = mgr_set_attr(
             pque->qu_attr,
             que_attr_def,
             QA_ATR_LAST,
             plist,
             preq->rq_perm,
             &bad,
             (void *)pque,
             ATR_ACTION_ALTER);

      if (rc != 0)
        {
        que_mutex.unlock();
        reply_badattr(rc, bad, plist, preq);
        return;
        }

      que_save(pque);

      mgr_log_attr(msg_man_set, plist, PBS_EVENTCLASS_QUEUE, pque->qu_qs.qu_name);

      if (allques == FALSE)
        break;

      }

    pque = next_queue(&svr_queues,&iter);
    }  /* END while (pque != NULL) */

  /* check the appropriateness of the attributes based on queue type */
  iter = -1;

  if (allques == TRUE)
    pque = next_queue(&svr_queues,&iter);

  while (pque != NULL)
    {
      {
      mutex_mgr que_mutex(pque->qu_mutex, true);
      if ((badattr = check_que_attr(pque)) != NULL)
        {
        sprintf(log_buf, msg_attrtype, pque->qu_qs.qu_name, badattr);
        que_mutex.unlock();
        reply_text(preq, PBSE_ATTRTYPE, log_buf);
        return;
        }

      if (allques == FALSE)
        break;

      }
    pque = next_queue(&svr_queues,&iter);
    }  /* END while (pque != NULL) */

  reply_ack(preq);

  return;
  }  /* END mgr_queue_set() */





/*
 * mgr_queue_unset - Unset (clear)  Queue Attribute Values
 *
 * Finds the queue, clears the requested attributes and returns a reply
 */

void mgr_queue_unset(

  struct batch_request *preq)

  {
  int    allques;
  int    bad_attr = 0;
  svrattrl  *plist;
  pbs_queue *pque;
  const char      *qname;
  int    rc;
  int    iter = -1;
  char   log_buf[LOCAL_LOG_BUF_SIZE];

  if (LOGLEVEL >= 7)
    {
    sprintf(log_buf, "from user %s", preq->rq_user);
    log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_QUEUE, __func__, log_buf);
    }

  if ((*preq->rq_ind.rq_manager.rq_objname == '\0') ||
      (*preq->rq_ind.rq_manager.rq_objname == '@'))
    {
    qname   = all_quename;
    allques = TRUE;
    pque = next_queue(&svr_queues,&iter);
    }
  else
    {
    allques = FALSE;
    qname   = preq->rq_ind.rq_manager.rq_objname;

    pque = find_queuebyname(qname);
    }

  if (pque == NULL)
    {
    req_reject(PBSE_UNKQUE, 0, preq, NULL, NULL);

    return;
    }

  sprintf(log_buf, msg_manager, msg_man_uns, preq->rq_user, preq->rq_host);
  log_event(PBSEVENT_ADMIN,PBS_EVENTCLASS_QUEUE,qname,log_buf);

  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  while (pque != NULL)
    {
      {
      mutex_mgr que_mutex(pque->qu_mutex, true);

      rc = mgr_unset_attr(pque->qu_attr, que_attr_def, QA_ATR_LAST, plist, preq->rq_perm, &bad_attr);

      if (rc != 0)
        {
        que_mutex.unlock();
        reply_badattr(rc, bad_attr, plist, preq);
        return;
        }

      que_save(pque);

      mgr_log_attr(msg_man_uns, plist, PBS_EVENTCLASS_QUEUE, pque->qu_qs.qu_name);

      if ((pque->qu_attr[QA_ATR_QType].at_flags & ATR_VFLAG_SET) == 0)
        pque->qu_qs.qu_type = QTYPE_Unset;
      }
    if (allques == FALSE)
      break;
    
    pque = next_queue(&svr_queues,&iter);
    }

  reply_ack(preq);

  return;
  } /* END mgr_queue_unset() */





/*
 * mgr_node_set - Set node "state" or "properties"
 *
 * Finds the node, Sets the requested state flags, properties, or type and returns
 * a reply to the sender of the batch_request
 */

void mgr_node_set(

  struct batch_request *preq)  /* I */

  {
  int               need_todo = 0;

  int               check_all = 0;
  int               propnodes = 0;
  int               rc;
  int               bad = 0;
  int               i;
  int               len;
  int               problem_cnt = 0;

  const char       *nodename = NULL;
  char             *problem_names;
  char              log_buf[LOCAL_LOG_BUF_SIZE];

  svrattrl         *plist;
  node_check_info   nci;
  node_iterator     iter;

  struct pbsnode   *pnode = NULL;
  struct pbsnode  **problem_nodes = NULL;
  struct prop       props;

  if ((*preq->rq_ind.rq_manager.rq_objname == '\0') ||
      (*preq->rq_ind.rq_manager.rq_objname == '@') ||
      ((*preq->rq_ind.rq_manager.rq_objname == ':') &&
       (*(preq->rq_ind.rq_manager.rq_objname + 1) != '\0')))
    {
    /*In this instance the set node req is to apply to all */
    /*nodes at the local ('\0')  or specified ('@') server */

    if (svr_totnodes)
      {
      pnode = (struct pbsnode *)1;

      if ((*preq->rq_ind.rq_manager.rq_objname == ':') &&
          (strcmp(preq->rq_ind.rq_manager.rq_objname + 1, "ALL") != 0))
        {
        propnodes = 1;
        nodename = preq->rq_ind.rq_manager.rq_objname;
        props.name = (char *)nodename + 1;
        props.mark = 1;
        props.next = NULL;
        }
      else
        {
        check_all = 1;
        nodename = all_nodes_str;
        }
      }
    }
  else
    {
    nodename = preq->rq_ind.rq_manager.rq_objname;
    pnode = find_nodebyname(nodename);
    }

  if (pnode == NULL)
    {
    req_reject(PBSE_UNKNODE, 0, preq, NULL, NULL);

    return;
    }

  /*set "state", "properties", or type of node (nodes if check_all == 1)*/

  sprintf(log_buf, msg_manager, msg_man_set, preq->rq_user, preq->rq_host);

  log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_NODE, nodename, log_buf);

  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  if (check_all || propnodes)
    {
    /* handle scrolling over all nodes */
    problem_nodes = (struct pbsnode **)calloc(svr_totnodes, sizeof(struct pbsnode *));

    problem_cnt = 0;
  
    reinitialize_node_iterator(&iter);
    pnode = NULL;

    while ((pnode = next_node(&allnodes,pnode,&iter)) != NULL)
      {
      if ((propnodes == TRUE) && 
          (!hasprop(pnode, &props)))
        {
        continue;
        }

      save_characteristic(pnode,&nci);

      rc = mgr_set_node_attr(
             pnode,
             node_attr_def,
             ND_ATR_LAST,
             plist,
             preq->rq_perm,
             &bad,
             (void *)pnode,
             ATR_ACTION_ALTER);

      if (rc != 0)
        {
        if (problem_nodes) /* we have an array in which to save*/
          problem_nodes[problem_cnt] = pnode;
        
        ++problem_cnt;
        }
      else
        {
        /* modifications succeeded for this node */
        chk_characteristic(pnode, &nci, &need_todo);

        mgr_log_attr(msg_man_set, plist, PBS_EVENTCLASS_NODE, pnode->nd_name);
        }
      }  /* END for each node */

    } /* END multiple node case */
  else
    {
    /* handle single node case */
    save_characteristic(pnode,&nci);

    rc = mgr_set_node_attr(
           pnode,
           node_attr_def,
           ND_ATR_LAST,
           plist,
           preq->rq_perm,
           &bad,
           (void *)pnode,
           ATR_ACTION_ALTER);

    if (rc != 0)
      {
      /* In the specific node case, reply w/ error and return*/
      
      switch (rc)
        {
        case PBSE_INTERNAL:
          
        case PBSE_SYSTEM:

          req_reject(rc, bad, preq, NULL, NULL);
          
          break;

        case PBSE_NOATTR:
          
        case PBSE_ATTRRO:
          
        case PBSE_MUTUALEX:
          
        case PBSE_BADNDATVAL:
          
          reply_badattr(rc, bad, plist, preq);
          
          break;
          
        default:
          
          req_reject(rc, 0, preq, NULL, NULL);
          
          break;
        }

      unlock_node(pnode, "mgr_node_set", (char *)"error", LOGLEVEL);
      
      return;
      } /* END if (rc != 0) */ 
    else
      {
      /* modifications succeeded for this node */
      chk_characteristic(pnode, &nci, &need_todo);
      
      mgr_log_attr(msg_man_set, plist, PBS_EVENTCLASS_NODE, pnode->nd_name);
      }

    unlock_node(pnode, "mgr_node_set", (char *)"single_node", LOGLEVEL);
    } /* END single node case */

  if (need_todo & WRITENODE_STATE)
    {
    /*some nodes set to "offline"*/
    write_node_state();

    need_todo &= ~(WRITENODE_STATE);
    }

  if (need_todo & WRITENODE_NOTE)
    {
    /*some nodes have new "note"s*/
    write_node_note();

    need_todo &= ~(WRITENODE_NOTE);
    }

  if (need_todo & WRITE_NEW_NODESFILE)
    {
    /*create/delete/prop/ntype change*/
    if (!update_nodes_file(NULL))
      need_todo &= ~(WRITE_NEW_NODESFILE);  /*successful on update*/
    }

  if (check_all || propnodes)
    {
    /* modification was for all nodes */
    if (problem_cnt)
      {
      /* one or more problems encountered */

      for (len = 0, i = 0;i < problem_cnt;i++)
        len += strlen(problem_nodes[i]->nd_name) + 3;

      len += strlen(pbse_to_txt(PBSE_GMODERR));

      if ((problem_names = (char *)calloc(1, len)))
        {
        strcpy(problem_names, pbse_to_txt(PBSE_GMODERR));

        for (i = 0;i < problem_cnt;i++)
          {
          if (i)
            strcat(problem_names, ", ");

          strcat(problem_names, problem_nodes[i]->nd_name);
          }

        reply_text(preq, PBSE_GMODERR, problem_names);

        free(problem_names);
        }
      else
        {
        reply_text(preq, PBSE_GMODERR, pbse_to_txt(PBSE_GMODERR));
        }
      }

    if (problem_nodes != NULL)
      free(problem_nodes);

    if (problem_cnt)
      {
      /* reply has already been sent */

      recompute_ntype_cnts();

      return;
      }
    }    /* END if (check_all || propnodes) */

  recompute_ntype_cnts();

  reply_ack(preq);  /*request completely successful*/

  return;
  }  /* END void mgr_node_set() */





/*
 * mgr_node_delete - mark a node (or all nodes) in the server's node list
 *                   as being "deleted".  It (they) will no longer get
 *                   assigned to a job, will no longer be pinged, and
 *                   any current job tasks will continue until they end,
 *                   abort, or are killed.
 *
 */

static void mgr_node_delete(

  struct batch_request *preq)

  {
  int             check_all = 0;
  int             iter;

  struct pbsnode *pnode;
  const char    *nodename = NULL;


  svrattrl       *plist;

  char            log_buf[LOCAL_LOG_BUF_SIZE];

  if ((*preq->rq_ind.rq_manager.rq_objname == '\0') ||
      (*preq->rq_ind.rq_manager.rq_objname == '@'))
    {
    /* In this instance the set node req is to apply to all */
    /* nodes at the local ('\0')  or specified ('@') server */

    if (svr_totnodes)
      {
      nodename = all_nodes_str;
      pnode = (struct pbsnode *)1;
      check_all = 1;
      }
    else
      {
      /* specified server has no nodes in its node table */
      pnode = NULL;
      }
    }
  else
    {
    nodename = preq->rq_ind.rq_manager.rq_objname;

    pnode = find_nodebyname(nodename);
    }

  if (pnode == NULL)
    {
    req_reject(PBSE_UNKNODE, 0, preq, NULL, NULL);

    return;
    }

  sprintf(log_buf, msg_manager, msg_man_del, preq->rq_user, preq->rq_host);

  log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_NODE, nodename, log_buf);

  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  /*if doing many and problem arises with some, record them for report*/
  /*the array of "problem nodes" sees no use now and may never see use*/

  if (check_all)
    {
    /* handle all nodes */
    iter = -1;

    while ((pnode = next_host(&allnodes,&iter,NULL)) != NULL)
      {
      snprintf(log_buf,sizeof(log_buf),"%s",pnode->nd_name);

      effective_node_delete(&pnode);

      mgr_log_attr(msg_man_set, plist, PBS_EVENTCLASS_NODE, log_buf);
      } /* end loop ( all nodes ) */
    }
  else
    {
    /* handle single nodes */
    snprintf(log_buf,sizeof(log_buf),"%s",pnode->nd_name);

    effective_node_delete(&pnode);

    mgr_log_attr(msg_man_set, plist, PBS_EVENTCLASS_NODE, log_buf);
    }

  /*set "deleted" bit in node's (nodes, check_all == 1) "inuse" field*/
  /*remove entire prop list, including the node name, from the node */
  /*remove the IP address array hanging from the node               */

  /* update the nodes file and node state since we deleted node(s) */
  write_node_state();
  update_nodes_file(NULL);

  recompute_ntype_cnts();

  reply_ack(preq);  /*request completely successful*/

  return;
  }  /* END mgr_node_delete() */





/*
 * mgr_node_create - process request to create a node
 *
 * Creates pbsnode and calls mgr_set_node_attr to set
 *      any associated node attributes also specified in the
 *      request.
 */

void mgr_node_create(

  struct batch_request *preq)

  {
  int   bad;
  svrattrl *plist;
  int   rc;

  plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

  rc = create_pbs_node(
         preq->rq_ind.rq_manager.rq_objname,
         plist,
         preq->rq_perm,
         &bad);

  if (rc != 0)
    {
    switch (rc)
      {

      case PBSE_INTERNAL:

      case PBSE_SYSTEM:

        req_reject(rc, bad, preq, NULL, NULL);

        break;

      case PBSE_NOATTR:

      case PBSE_ATTRRO:

      case PBSE_MUTUALEX:

      case PBSE_BADNDATVAL:

        reply_badattr(rc, bad, plist, preq);

        break;

      default:

        req_reject(rc, 0, preq, NULL, NULL);

        break;
      }

    return;
    }

  mgr_log_attr(
    msg_man_set,
    plist,
    PBS_EVENTCLASS_NODE,
    preq->rq_ind.rq_manager.rq_objname);

  setup_notification(preq->rq_ind.rq_manager.rq_objname);     /* set mechanism for notifying */
  /* other nodes of new member   */

  if (update_nodes_file(NULL))
    {
    /* if update fails now (odd) */

    svr_chngNodesfile = 1;  /* try it when server shutsdown */
    }
  else
    {
    svr_chngNodesfile = 0;
    }

  reply_ack(preq);     /* create request successful */

  return;
  }




/*
 * req_manager - the dispatch routine for a series of functions which
 * implement the Manager (qmgr) Batch Request
 *
 * The privilege of the requester is checked against the type of
 * the object and the operation to be performed on it.  Then the
 * appropriate function is called to perform the operation.
 */

int req_manager(
    struct batch_request *preq)
  {
  int rc = PBSE_NONE;
  char log_buf[LOCAL_LOG_BUF_SIZE+1];

  switch (preq->rq_ind.rq_manager.rq_cmd)
    {

    case MGR_CMD_CREATE:

    case MGR_CMD_DELETE:

      if ((preq->rq_perm & PERM_MANAGER) == 0)
        {
        rc = PBSE_PERM;
        snprintf(log_buf, LOCAL_LOG_BUF_SIZE,
            "error in permissions (PERM_MANAGER)");
        req_reject(rc, 0, preq, NULL, log_buf);
        return rc;
        }

      switch (preq->rq_ind.rq_manager.rq_objtype)
        {

        case MGR_OBJ_QUEUE:

          if (preq->rq_ind.rq_manager.rq_cmd == MGR_CMD_CREATE)
            mgr_queue_create(preq);
          else
            mgr_queue_delete(preq);

          break;

        case MGR_OBJ_NODE:

          if (preq->rq_ind.rq_manager.rq_cmd == MGR_CMD_CREATE)
            mgr_node_create(preq);
          else
            mgr_node_delete(preq);

          break;

        default:

          rc = PBSE_IVALREQ;
          snprintf(log_buf, LOCAL_LOG_BUF_SIZE, "Invalid object type");
          req_reject(rc, 0, preq, NULL, log_buf);
          return rc;
          break;
        }

      break;

    case MGR_CMD_SET:

      if ((preq->rq_perm & PERM_OPorMGR) == 0)
        {
        rc = PBSE_PERM;
        snprintf(log_buf, LOCAL_LOG_BUF_SIZE,
            "error in permissions (PERM_OPorMGR)");
        req_reject(rc, 0, preq, NULL, log_buf);
        return rc;
        }

      switch (preq->rq_ind.rq_manager.rq_objtype)
        {

        case MGR_OBJ_SERVER:

          mgr_server_set(preq);

          break;

        case MGR_OBJ_QUEUE:

          mgr_queue_set(preq);

          break;

        case MGR_OBJ_NODE:

          mgr_node_set(preq);

          break;

        default:

          req_reject(PBSE_IVALREQ, 0, preq, NULL, NULL);

          break;
        }

      break;

    case MGR_CMD_UNSET:

      if ((preq->rq_perm & PERM_OPorMGR) == 0)
        {
        rc = PBSE_PERM;
        snprintf(log_buf, LOCAL_LOG_BUF_SIZE,
            "error in permissions (PERM_OPorMGR)");
        req_reject(rc, 0, preq, NULL, log_buf);
        return rc;
        }

      switch (preq->rq_ind.rq_manager.rq_objtype)
        {

        case MGR_OBJ_SERVER:

          mgr_server_unset(preq);

          break;

        case MGR_OBJ_QUEUE:

          mgr_queue_unset(preq);

          break;

#if 0

          /* "unsetting" nodes isn't currently supported */

        case MGR_OBJ_NODE:

          mgr_node_unset(preq);

          break;

#endif /* 0 */

        default:

          req_reject(PBSE_IVALREQ, 0, preq, NULL, NULL);

          break;
        }

      break;

    default: /* batch_request specified an invalid command */

      req_reject(PBSE_IVALREQ, 0, preq, NULL, NULL);

      break;
    }  /* END switch() */

  return rc;
  }  /* END req_manager() */



/*
 * manager_oper_chk - check the @host part of a manager or operator acl
 * entry to insure it is fully qualified.  This is to prevent
 * input errors when setting the list.
 * This is the at_action() routine for the server attributes
 * "managers" and "operators"
 */

int manager_oper_chk(

  pbs_attribute *pattr,
  void          *pobject,
  int            actmode)

  {
  char   *entry;
  int    err = 0;
  int      i;

  struct array_strings *pstr;
  char                  log_buf[LOCAL_LOG_BUF_SIZE];

  if (actmode == ATR_ACTION_FREE)
    {
    return(0); /* no checking on free */
    }

  pstr = pattr->at_val.at_arst;

  if (pstr == NULL)
    {
    return(0);
    }

  for (i = 0;i < pstr->as_usedptr;++i)
    {
    entry = strchr(pstr->as_string[i], (int)'@');

    if (entry == NULL)
      {
      err = PBSE_BADHOST;

      break;
      }

    entry++; /* point after the '@' */

    if (hostname_check(entry) == FAILURE)
      {
      if (actmode == ATR_ACTION_RECOV)
        {
        sprintf(log_buf, "bad entry in acl: %s",
          pstr->as_string[i]);

        log_err(PBSE_BADACLHOST, "manager_oper_chk", log_buf);
        }
      else
        {
        sprintf(log_buf, "bad entry in acl: %s", pstr->as_string[i]);

        log_err(PBSE_BADACLHOST, "manager_oper_chk", log_buf);

        err = PBSE_BADACLHOST;
        }
      }
    }

  return(err);
  }  /* END manager_oper_chk() */

/*
 * servername_chk - insure it is fully qualified.
 * This is the at_action() routine for the server attribute
 * "server_name"
 */

int servername_chk(

  pbs_attribute *pattr,   /* I */
  void          *pobject, /* I */
  int           actmode) /* I */

  {
  int    err = 0;
  char    hostname[PBS_MAXSERVERNAME + 1];
  char *pstr;

  extern char              server_name[];
  char         log_buf[LOCAL_LOG_BUF_SIZE];

  if (actmode == ATR_ACTION_FREE)
    {
    if ((gethostname(hostname, PBS_MAXHOSTNAME) == -1) ||
        (get_fullhostname(hostname, hostname, PBS_MAXHOSTNAME, NULL) == -1))
      {
      return(PBSE_BADHOST);
      }

    strcpy(server_name, hostname);

    return(0);
    }

  pstr = pattr->at_val.at_str;

  if (pstr == NULL)
    {
    return(PBSE_BADHOST);
    }

  /* must be fully qualified host */

  if (get_fullhostname(pstr, hostname, PBS_MAXHOSTNAME, NULL) ||
      strncmp(pstr, hostname, PBS_MAXHOSTNAME))
    {
    if (actmode == ATR_ACTION_RECOV)
      {
      sprintf(log_buf, "bad servername: %s", pstr);

      log_err(PBSE_BADHOST,"servername_chk",log_buf);
      }
    else
      {
      err = PBSE_BADHOST;
      }
    }
  else
    {
    strcpy(server_name, hostname);
    }

  return(err);
  }  /* END server_name_chk() */


/*
 * extra_resc_chk() called when extra_resc server pbs_attribute is changed
 *     It's purpose is to re-init the resource definitions by calling
 *     init_resc_defs().  Unfortunately, it gets call before the
 *     change is actually committed; so we setup a work task to call
 *     it async asap.
 */
void on_extra_resc(

  struct work_task *ptask)

  {
  init_resc_defs();

  free(ptask->wt_mutex);
  free(ptask);
  }

int extra_resc_chk(

  pbs_attribute *pattr,   /* I */
  void          *pobject, /* I */
  int            actmode) /* I */

  {
  /* Is there anything to validate?  Maybe check for all alphanum? */
  /* the new resource is at pattr->at_val.at_str */
  set_task(WORK_Immed, 0, on_extra_resc, (char *)NULL, FALSE);

  return(TRUE);
  }

/*
 * free_extraresc() makes sure that the init_resc_defs() is called after
 * the list has changed by 'unset'.
 */
void free_extraresc(

  pbs_attribute *attr)

  {
  set_task(WORK_Immed, 0, on_extra_resc, (char *)NULL, FALSE);

  free_arst(attr);
  }


/*
 * disallowed_types_chk -
 *      This is the at_action() routine for the queue disallowed_type pbs_attribute
 */

int disallowed_types_chk(

  pbs_attribute *pattr,
  void          *pobject,
  int            actmode)

  {
  int i;
  int j;
  int found;

  struct array_strings *pstr;
  extern char *array_disallowed_types[];

  if (actmode == ATR_ACTION_FREE)
    {
    return(0);  /* no checking on free or DECR */
    }

  pstr = pattr->at_val.at_arst;

  if (pstr == NULL)
    {
    return(0);
    }

  for (i = 0;i < pstr->as_usedptr;i++)
    {
    /* first check for dupe */
    for (j = 0; j < pstr->as_usedptr;j++)
      {
      if (i == j)
        continue;

      if (strcmp(pstr->as_string[i], pstr->as_string[j]) == 0)
        {
        return PBSE_DUPLIST;
        }
      }

    found = FALSE;

    for (j = 0; (strcmp(array_disallowed_types[j], "_END_") != 0); j++)
      {
      if (strcmp(pstr->as_string[i], array_disallowed_types[j]) == 0)
        {
        found = TRUE;
        break;
        }
      }

    if (found == FALSE)
      {
      return(PBSE_BADDISALLOWTYPE);
      }
    }

  return(0);
  } /* END disallowed_types_chk() */


static int mgr_long_action_helper(

  pbs_attribute *pattr,
  int            actmode,
  long           minimum,
  long           maximum)

  {
  int err = PBSE_NONE;

  if (pattr->at_val.at_long < minimum)
    {
    err = PBSE_BADATVAL;
    }

  if (pattr->at_val.at_long > maximum)
    {
    err = PBSE_EXLIMIT;
    }

  if (err && (actmode == ATR_ACTION_RECOV))
    {
    pattr->at_val.at_long = maximum;
    err = PBSE_NONE;
    }

  return(PBSE_NONE);
  }  /* END mgr_long_action_helper() */





/*
 * schiter_chk - make sure values are sane
 * This is the at_action() routine for the server pbs_attribute
 * "scheduler_iteration"
 */

int schiter_chk(

  pbs_attribute *pattr,
  void          *pobject,
  int            actmode)

  {
  return(mgr_long_action_helper(pattr, actmode, 1, PBS_SCHEDULE_CYCLE));
  }  /* END schiter_chk() */



/* nextjobnum_action - makes sure value is sane (>=0)
   Note that there must be special code in svr_recov
   to prevent the pbs_attribute value from clobbering
   the internal counter server.sv_qs.sv_jobidnumber.
 */
int nextjobnum_chk(

  pbs_attribute *pattr,
  void          *pobject,
  int            actmode)

  {
  if (pattr->at_val.at_long > PBS_SEQNUMTOP)
    {
    return(PBSE_EXLIMIT);
    }
  else if (pattr->at_val.at_long >= 0)
    {
    return(PBSE_NONE);
    }
  else
    {
    return(PBSE_BADATVAL);
    }
  }

int set_nextjobnum(

  pbs_attribute *attr,
  pbs_attribute *new_attr,
  enum batch_op  op)

  {
  char log_buf[LOCAL_LOG_BUF_SIZE + 1];
  /* this is almost identical to set_l, but we need to grab the
     current value of server.sv_qs.sv_jobidnumber before we INCR/DECR the value of
     attr->at_val.at_long.  In fact, it probably should be moved to Libattr/ */

  /* The special handling of INCR is to allow setting of a job number range.
   * If the job numbers are already in the range, it does not alter the number.
   * Otherwise, if the number is in the default range, it sets it to the new range.
   */

  lock_sv_qs_mutex(server.sv_qs_mutex, __func__);
  switch (op)
    {

    case SET:
    
      attr->at_val.at_long = new_attr->at_val.at_long;
    
      break;

      /* Code in Moab depends on this, do not change until the Moab code is fixed. */

    case INCR:


      attr->at_val.at_long = MAX(server.sv_qs.sv_jobidnumber, new_attr->at_val.at_long);
      
      break;

    case DECR:
      
      attr->at_val.at_long -= new_attr->at_val.at_long;
      
      break;

    default:
     
      sprintf(log_buf, "%s:1", __func__);
      unlock_sv_qs_mutex(server.sv_qs_mutex, log_buf);
      return(PBSE_SYSTEM);
    }

  attr->at_flags |= ATR_VFLAG_SET | ATR_VFLAG_MODIFY;

  server.sv_qs.sv_jobidnumber = attr->at_val.at_long;

  sprintf(log_buf, "%s:2", __func__);
  unlock_sv_qs_mutex(server.sv_qs_mutex, log_buf);

  return(PBSE_NONE);
  } /* END set_nextjobnum() */

/* END req_manager.c */

