/*
*         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.
*/
#include <pbs_config.h>   /* the master config generated by configure */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <tcl.h>

#include "pbs_error.h"
#include "pbs_ifl.h"
#include "log.h"
#include "../lib/Liblog/pbs_log.h"
#include "resmon.h"
#include "cmds.h"
#include "rm.h"

extern void site_cmds(Tcl_Interp *);

char badparm[] = "%s: bad parameter";
char missingfd[] = "%s: missing file descriptor";
char not_connected[] = "not connected";
char fail[] = "failed";
Tcl_Obj *pbserr;

int connector = -1;

char *getreq_err(int *local_errno, int  stream);
struct batch_status *pbs_statserver_err(int c, struct attrl *attrib, char *extend, int *local_errno);
struct batch_status *pbs_statjob_err(int c, char *id, struct attrl *attrib, char *extend, int *local_errno);
struct batch_status *pbs_selstat_err(int c, struct attropl *attrib, char *extend, int *local_errno);
struct batch_status *pbs_statnode_err(int c, char *id, struct attrl *attrib, char *extend, int *local_errno);
struct batch_status *pbs_statque_err(int c, char *id, struct attrl *attrib, char *extend, int *local_errno);
int pbs_rerunjob_err(int c, char *jobid, char *extend, int *local_errno);
int pbs_movejob_err(int c, char *jobid, char *destin, char *extend, int *local_errno);
int pbs_asyrunjob_err(int c, char *jobid, char *location, char *extend, int *local_errno);
int pbs_runjob_err(int c, char *jobid, char *location, char *extend, int *rc);
int pbs_deljob_err(int c, char *jobid, char *extend, int *local_errno);
int pbs_holdjob_err(int c, char *jobid, char *holdtype, char *extend, int *local_errno);
int pbs_manager_err(int c, int command, int objtype, char *objname, struct attropl *attrib, char *extend, int *local_errno);
int pbs_alterjob_err(int c, char *jobid, struct attrl *attrib, char *extend, int *local_errno);

#define SET_PBSERR(value) \
  (void)Tcl_ObjSetVar2(interp, pbserr, NULL, \
                       Tcl_NewIntObj((value)), TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)

int OpenRM(
    
  ClientData     clientData,
  Tcl_Interp    *interp,
  int            objc, 
  Tcl_Obj *CONST objv[])

  {
  int     port = 0;
  int fd;
  char *host;

  if (objc == 3)
    {
    if (Tcl_GetIntFromObj(interp, objv[2], &port) != TCL_OK)
      return TCL_ERROR;
    }
  else if (objc != 2)
    {
    Tcl_WrongNumArgs(interp, 1, objv, "host ?port?");
    return TCL_ERROR;
    }

  host = Tcl_GetStringFromObj(objv[1], NULL);

  if ((fd = openrm(host, port)) < 0)
    {
    Tcl_PosixError(interp);
    log_err(-1, Tcl_GetStringFromObj(objv[0], NULL), host);
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(fd));
  return TCL_OK;
  }

int CloseRM(
    
  ClientData     clientData,
  Tcl_Interp    *interp,
  int            objc,
  Tcl_Obj *CONST objv[])
  
  {
  int fd, ret;
  char *cmd;
  int   local_errno = 0;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (objc != 2)
    {
    sprintf(log_buffer, missingfd, cmd);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (Tcl_GetIntFromObj(interp, objv[1], &fd) != TCL_OK)
    return TCL_ERROR;

  if ((ret = closerm_err(&local_errno, fd)) == -1)
    {
    Tcl_PosixError(interp);
    log_err(local_errno, cmd, Tcl_GetStringFromObj(objv[1], NULL));
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
  return TCL_OK;
  }

int DownRM(
    
  ClientData     clientData,
  Tcl_Interp    *interp,
  int            objc,
  Tcl_Obj *CONST objv[])

  {
  int fd, ret;
  char *cmd;
  int   local_errno = 0;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (objc != 2)
    {
    sprintf(log_buffer, missingfd, cmd);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (Tcl_GetIntFromObj(interp, objv[1], &fd) != TCL_OK)
    return TCL_ERROR;

  if ((ret = downrm(&local_errno, fd)) == -1)
    {
    Tcl_PosixError(interp);
    log_err(-1, cmd, Tcl_GetStringFromObj(objv[1], NULL));
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
  return TCL_OK;
  }

int ConfigRM(
    
  ClientData     clientData,
  Tcl_Interp    *interp,
  int            objc,
  Tcl_Obj *CONST objv[])
  
  {
  int fd, ret;
  int local_errno = 0;
  char *cmd, *filename;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (objc != 3)
    {
    sprintf(log_buffer,
            "%s: missing file descriptor or filename", cmd);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (Tcl_GetIntFromObj(interp, objv[1], &fd) != TCL_OK)
    return TCL_ERROR;

  filename = Tcl_GetStringFromObj(objv[2], NULL);

  ret = configrm(fd, &local_errno, filename);

  if (ret == -1)
    {
    Tcl_PosixError(interp);
    log_err(local_errno, cmd, filename);
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
  return TCL_OK;
  }

int AddREQ(
    
  ClientData    clientData,
  Tcl_Interp   *interp,
  int           objc,
  Tcl_Obj *CONST objv[])
  
  {
  int fd, ret;
  int local_errno = 0;
  char *cmd, *request;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (objc != 3)
    {
    sprintf(log_buffer,
            "%s: missing file descriptor or request", cmd);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (Tcl_GetIntFromObj(interp, objv[1], &fd) != TCL_OK)
    return TCL_ERROR;

  request = Tcl_GetStringFromObj(objv[2], NULL);

  ret = addreq_err(fd, &local_errno, request);

  if (ret == -1)
    {
    Tcl_PosixError(interp);
    log_err(local_errno, cmd, request);
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
  return TCL_OK;
  }

int AllREQ(
    
  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  int ret;

  if (argc != 2)
    {
    sprintf(log_buffer, "%s: missing request", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  ret = allreq(strdup(argv[1]));

  Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
  return TCL_OK;
  }

int GetREQ(
  
  ClientData     clientData,
  Tcl_Interp    *interp,
  int            objc,
  Tcl_Obj *CONST objv[])
  
  {
  int fd;
  int local_errno = 0;
  char *ret;
  char *cmd;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (objc != 2)
    {
    sprintf(log_buffer, missingfd, cmd);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (Tcl_GetIntFromObj(interp, objv[1], &fd) != TCL_OK)
    return TCL_ERROR;

  if ((ret = getreq_err(&local_errno, fd)) == NULL)
    {
    if (local_errno)
      {
      Tcl_PosixError(interp);
      log_err(local_errno, cmd,
              Tcl_GetStringFromObj(objv[1], NULL));
      }
    }
  else
    {
    int err = 0;

    Tcl_SetResult(interp, ret, (Tcl_FreeProc *)free);

    if (*ret == '?')
      {
      if (strlen(ret) > (size_t)2 && /* look for err num */
          Tcl_GetInt(interp, &ret[2], &err) != TCL_OK)
        return TCL_ERROR;

      }

    SET_PBSERR(err);
    }

  return TCL_OK;
  }

int FlushREQ(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  if (argc != 1)
    {
    sprintf(log_buffer, badparm, argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  flushreq();
  
  return TCL_OK;
  }

int ActiveREQ(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  int ret;

  if (argc != 1)
    {
    sprintf(log_buffer, badparm, argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  ret = activereq();

  if (ret == -1)
    {
    Tcl_PosixError(interp);
    sprintf(log_buffer, "result %d", ret);
    log_err(-1, argv[0], log_buffer);
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
  return TCL_OK;
  }

int FullResp(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  int flag;

  if (argc != 2)
    {
    sprintf(log_buffer, "%s: missing flag", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (Tcl_GetBoolean(interp, argv[1], &flag) != TCL_OK)
    return TCL_ERROR;

  fullresp(flag);

  SET_PBSERR(0);

  return TCL_OK;
  }

int PBS_Connect(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *server = NULL;

  if (argc == 2)
    server = strdup(argv[1]);
  else if (argc != 1)
    {
    sprintf(log_buffer, "%s: wrong # args: ?server?", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector >= 0)
    pbs_disconnect(connector);

  if ((connector = pbs_connect(server)) < 0)
    {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    sprintf(log_buffer, "%s (%d)",
            server ? server : "DefaultServer",
            connector * -1);
    log_err(-1, argv[0], log_buffer);
    }
  else
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

  return TCL_OK;
  }

int PBS_Disconnect(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;

  if (argc != 1)
    {
    sprintf(log_buffer, badparm, argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector >= 0 && pbs_disconnect(connector))
    {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, -1);
    log_err(-1, argv[0], log_buffer);
    }
  else
    {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
    connector = -1;
    }

  return TCL_OK;
  }

Tcl_Obj *attrlist(
    
  Tcl_Interp   *interp,
  struct attrl *ap)
  
  {
  Tcl_Obj *ret;

  ret = Tcl_NewListObj(0, NULL); /* null list */

  while (ap)
    {
    Tcl_Obj *twol[2];

    twol[0] = Tcl_NewStringObj(ap->name, -1);

    if (ap->resource)
      {
      Tcl_AppendStringsToObj(twol[0],
                             TCL_ATRSEP, ap->resource, NULL);
      }

    twol[1] = Tcl_NewStringObj(ap->value, -1);

    Tcl_ListObjAppendElement(interp, ret, Tcl_NewListObj(2, twol));

    ap = ap->next;
    }

  return (ret);
  }

void batresult(
    
  Tcl_Interp          *interp,
  struct batch_status *bs)
  
  {
  Tcl_Obj *batchl;

  struct batch_status *bp;

  batchl = Tcl_NewObj();  /* empty list */

  for (bp = bs; bp; bp = bp->next)
    {
    Tcl_Obj *threel[3];

    threel[0] = Tcl_NewStringObj(bp->name, -1);
    threel[1] = attrlist(interp, bp->attribs);
    threel[2] = Tcl_NewStringObj(bp->text, -1);

    Tcl_ListObjAppendElement(interp, batchl,
                             Tcl_NewListObj(3, threel));

    }

  Tcl_SetObjResult(interp, batchl);

  pbs_statfree(bs);
  }

int PBS_StatServ(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  int   local_errno = 0;

  struct batch_status *bs;
  Tcl_Obj *threel[3];

  if (argc != 1)
    {
    sprintf(log_buffer, badparm, argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if ((bs = pbs_statserver_err(connector, NULL, NULL, &local_errno)) == NULL)
    {
    if (local_errno != PBSE_NONE)
      {
      msg = pbs_geterrmsg(connector);
      sprintf(log_buffer, "%s (%d)",
              msg ? msg : fail, local_errno);
      log_err(-1, argv[0], log_buffer);
      }
    }
  else
    {
    threel[0] = Tcl_NewStringObj(bs->name, -1);
    threel[1] = attrlist(interp, bs->attribs);
    threel[2] = Tcl_NewStringObj(bs->text, -1);

    Tcl_SetObjResult(interp, Tcl_NewListObj(3, threel));

    pbs_statfree(bs);
    }

  return TCL_OK;
  }

int PBS_StatJob(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  int   local_errno = 0;

  struct batch_status *bs;

  if (argc != 1)
    {
    sprintf(log_buffer, badparm, argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if ((bs = pbs_statjob_err(connector, NULL, NULL, NULL, &local_errno)) == NULL)
    {
    if (local_errno != PBSE_NONE)
      {
      msg = pbs_geterrmsg(connector);
      sprintf(log_buffer, "%s (%d)",
              msg ? msg : fail, local_errno);
      log_err(-1, argv[0], log_buffer);
      }
    }
  else
    batresult(interp, bs);

  return TCL_OK;
  }

int PBS_SelStat(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;

  struct batch_status *bs;
  int                  local_errno = 0;

  static struct attropl att1 =
    {
    NULL,
    strdup("queue_type"),
    NULL,
    strdup("E"),
    EQ
    };

  static struct attropl att2 =
    {
    &att1,
    strdup("job_state"),
    NULL,
    strdup("Q"),
    EQ
    };

  if (argc != 1)
    {
    sprintf(log_buffer, badparm, argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if ((bs = pbs_selstat_err(connector, &att2, NULL, &local_errno)) == NULL)
    {
    if (local_errno != PBSE_NONE)
      {
      msg = pbs_geterrmsg(connector);
      sprintf(log_buffer, "%s (%d)",
              msg ? msg : fail, local_errno);
      log_err(-1, argv[0], log_buffer);
      }
    }
  else
    batresult(interp, bs);

  return TCL_OK;
  }

int PBS_StatQue(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  int   local_errno = 0;

  struct batch_status *bs;

  if (argc != 1)
    {
    sprintf(log_buffer, badparm, argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if ((bs = pbs_statque_err(connector, NULL, NULL, NULL, &local_errno)) == NULL)
    {
    if (local_errno != PBSE_NONE)
      {
      msg = pbs_geterrmsg(connector);
      sprintf(log_buffer, "%s (%d)",
              msg ? msg : fail, local_errno);
      log_err(-1, argv[0], log_buffer);
      }
    }
  else
    batresult(interp, bs);

  return TCL_OK;
  }

int PBS_StatNode(
    
  ClientData    clientData,
  Tcl_Interp   *interp,
  int           objc,
  Tcl_Obj *CONST objv[])
  
  {
  char *msg, *cmd;
  char *node = NULL;
  int   local_errno = 0;

  struct batch_status *bs;

  if (objc == 2)
    node = Tcl_GetStringFromObj(objv[1], NULL);
  else if (objc != 1)
    {
    Tcl_WrongNumArgs(interp, 1, objv, "?node?");
    return TCL_ERROR;
    }

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (connector < 0)
    {
    log_err(-1, cmd, not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if ((bs = pbs_statnode_err(connector, node, NULL, NULL, &local_errno)) == NULL)
    {
    if (local_errno != PBSE_NONE)
      {
      msg = pbs_geterrmsg(connector);
      sprintf(log_buffer, "%s (%d)",
              msg ? msg : fail, local_errno);
      log_err(-1, cmd, log_buffer);
      }
    }
  else
    batresult(interp, bs);

  return TCL_OK;
  }

int PBS_AsyRunJob(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  char *location = NULL;
  int   local_errno = 0;

  if (argc == 3)
    location = strdup(argv[2]);
  else if (argc != 2)
    {
    sprintf(log_buffer,
            "%s: wrong # args: job_id ?location?", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

  if (pbs_asyrunjob_err(connector, strdup(argv[1]), location, NULL, &local_errno))
    {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, local_errno);
    log_err(-1, argv[0], log_buffer);
    }

  return TCL_OK;
  }


int PBS_RunJob(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  char *location = NULL;
  int   local_errno = 0;

  if (argc == 3)
    location = strdup(argv[2]);
  else if (argc != 2)
    {
    sprintf(log_buffer,
            "%s: wrong # args: job_id ?location?", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if (pbs_runjob_err(connector, strdup(argv[1]), location, NULL, &local_errno))
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, local_errno);
    log_err(-1, argv[0], log_buffer);
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    }
  else
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

  return TCL_OK;
  }

int PBS_ReRun(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  char *extend = strdup("0");
  int   local_errno = 0;

  if (argc != 2)
    {
    sprintf(interp->result,
            "%s: wrong # args: job_id", argv[0]);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  interp->result = strdup("0");

  if (pbs_rerunjob_err(connector, strdup(argv[1]), extend, &local_errno))
    {
    interp->result = strdup("-1");
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, local_errno);
    log_err(-1, argv[0], log_buffer);
    }
  else
    {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
    }

  sprintf(log_buffer, "%d", local_errno);

  return TCL_OK;
  }


int PBS_MoveJob(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  char *location = NULL;
  char job_id_out[PBS_MAXCLTJOBID];
  char server_out[MAXSERVERNAME];
  int   local_errno = 0;

  if (argc == 3)
    location = strdup(argv[2]);
  else if (argc != 2)
    {
    sprintf(log_buffer,
            "%s: wrong # args: job_id ?location?", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if (get_server(argv[1], job_id_out, sizeof(job_id_out), server_out, sizeof(server_out)))
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s", msg ? msg : fail);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (pbs_movejob_err(connector, job_id_out, location, NULL, &local_errno))
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, local_errno);
    log_err(-1, argv[0], log_buffer);
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    }
  else
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

  return TCL_OK;
  }

int PBS_DelJob(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  char *message = NULL;
  int   local_errno = 0;

  if (argc == 3)
    message = strdup(argv[2]);
  else if (argc != 2)
    {
    sprintf(log_buffer,
            "%s: wrong # args: job_id ?message?", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if (pbs_deljob_err(connector, strdup(argv[1]), message, &local_errno))
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, local_errno);
    log_err(-1, argv[0], log_buffer);
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    }
  else
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

  return TCL_OK;
  }


int PBS_HoldJob(

  ClientData     clientData,
  Tcl_Interp    *interp,
  int            argc,
  const char    *argv[])
  
  {
  char *msg;
  int   local_errno = 0;

  if (argc != 2)
    {
    sprintf(log_buffer,
            "%s: wrong # args: job_id", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if (pbs_holdjob_err(connector, strdup(argv[1]), strdup(SYSTEM_HOLD), NULL, &local_errno))
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, local_errno);
    log_err(-1, argv[0], log_buffer);
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    }
  else
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

  return TCL_OK;
  }

int PBS_QueueOp(

  ClientData      clientData,
  Tcl_Interp     *interp,
  int             argc,
  const char     *argv[],
  struct attropl *attr)
  
  {
  int merr;
  int   local_errno = 0;

  if (argc != 2)
    {
    sprintf(log_buffer,
            "%s: wrong # args: queue", argv[0]);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (connector < 0)
    {
    log_err(-1, argv[0], not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  merr = pbs_manager_err(connector, MGR_CMD_SET, MGR_OBJ_QUEUE, strdup(argv[1]), attr, NULL, &local_errno);

  if (merr != 0)
    {
    sprintf(log_buffer, "%s: %s", argv[0],
            pbs_geterrmsg(connector));
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
  return TCL_OK;
  }

int PBS_EnableQueue(

  ClientData      clientData,
  Tcl_Interp     *interp,
  int             argc,
  const char     *argv[])
  
  {

  static struct attropl attr =
    {
    NULL, strdup("enabled"), NULL, strdup("TRUE"), SET
    };

  return PBS_QueueOp(clientData, interp, argc, argv, &attr);
  }

int PBS_DisableQueue(

  ClientData      clientData,
  Tcl_Interp     *interp,
  int             argc,
  const char     *argv[])
  
  {

  static struct attropl attr =
    {
    NULL, strdup("enabled"), NULL, strdup("FALSE"), SET
    };

  return PBS_QueueOp(clientData, interp, argc, argv, &attr);
  }

int PBS_StartQueue(

  ClientData      clientData,
  Tcl_Interp     *interp,
  int             argc,
  const char     *argv[])

  {

  static struct attropl attr =
    {
    NULL, strdup("started"), NULL, strdup("TRUE"), SET
    };

  return PBS_QueueOp(clientData, interp, argc, argv, &attr);
  }

int PBS_StopQueue(

  ClientData      clientData,
  Tcl_Interp     *interp,
  int             argc,
  const char     *argv[])

  {

  static struct attropl attr =
    {
    NULL, strdup("started"), NULL, strdup("FALSE"), SET
    };

  return PBS_QueueOp(clientData, interp, argc, argv, &attr);
  }

int PBS_AlterJob(

  ClientData      clientData,
  Tcl_Interp     *interp,
  int             objc,
  Tcl_Obj *CONST  objv[])
  
  {
  char *msg;
  int i, num, tre, ret;
  Tcl_Obj **listp, **indp;
  int   local_errno = 0;

  struct attrl *attrs, *atp = NULL;
  char *cmd;

  if (objc != 3)
    {
    Tcl_WrongNumArgs(interp, 1, objv, "job_id attribute(s)");
    return TCL_ERROR;
    }

  if ((ret = Tcl_ListObjGetElements(interp, objv[2],
                                    &num, &listp)) != TCL_OK)
    return ret;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  attrs = NULL;

  for (i = 0; i < num; i++)
    {
    if ((ret = Tcl_ListObjGetElements(interp, listp[i],
                                      &tre, &indp)) != TCL_OK)
      goto done;

    if (tre != 3)
      {
      sprintf(log_buffer,
              "%s: bad attribute format: %s",
              cmd, Tcl_GetStringFromObj(listp[i], NULL));
      Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
      ret = TCL_ERROR;
      goto done;
      }

    atp = (struct attrl *)malloc(sizeof(struct attrl));

    atp->name = strdup(Tcl_GetStringFromObj(indp[0], NULL));
    atp->resource = strdup(Tcl_GetStringFromObj(indp[1], NULL));
    atp->value = strdup(Tcl_GetStringFromObj(indp[2], NULL));
    atp->next = attrs;
    attrs = atp;
    }

  if (connector < 0)
    {
    log_err(-1, cmd, not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    goto done;
    }

  if (pbs_alterjob_err(connector,
                   Tcl_GetStringFromObj(objv[1], NULL),
                   atp, NULL, &local_errno))
    {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s (%d)", msg ? msg : fail, local_errno);
    log_err(-1, cmd, log_buffer);
    }
  else
    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

done:
  for (atp = attrs; attrs; atp = attrs)
    {
    attrs = atp->next;
    free(atp->name);
    free(atp->resource);
    free(atp->value);
    free(atp);
    }

  return ret;
  }

int PBS_RescQuery(
    
  ClientData    clientData,
  Tcl_Interp   *interp,
  int           objc,
  Tcl_Obj *CONST objv[])
  
  {
  char *msg;
  int i, num, ret;
  Tcl_Obj **listp, *fourl[4], *retl;
  char *cmd;
  char **res_array;
  int *avail_array, *alloc_array, *reser_array, *down_array;

  if (objc != 2)
    {
    Tcl_WrongNumArgs(interp, 1, objv, "{resource1 resource2 ...}");
    return TCL_ERROR;
    }

  if ((ret = Tcl_ListObjGetElements(interp, objv[1],
                                    &num, &listp)) != TCL_OK)
    return ret;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (connector < 0)
    {
    log_err(-1, cmd, not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  res_array = (char **)malloc(sizeof(char *) * num);

  avail_array = (int *)malloc(sizeof(int) * num);
  alloc_array = (int *)malloc(sizeof(int) * num);
  reser_array = (int *)malloc(sizeof(int) * num);
  down_array = (int *)malloc(sizeof(int) * num);

  for (i = 0; i < num; i++)
    res_array[i] = Tcl_GetStringFromObj(listp[i], NULL);

  retl = Tcl_NewObj();  /* empty list */

  if (pbs_rescquery(connector, res_array, num,
                    avail_array, alloc_array, reser_array, down_array))
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s", msg ? msg : fail);
    log_err(-1, cmd, log_buffer);
    }
  else
    {
    for (i = 0; i < num; i++)
      {
      fourl[0] = Tcl_NewIntObj(avail_array[i]);
      fourl[1] = Tcl_NewIntObj(alloc_array[i]);
      fourl[2] = Tcl_NewIntObj(reser_array[i]);
      fourl[3] = Tcl_NewIntObj(down_array[i]);

      Tcl_ListObjAppendElement(interp, retl,
                               Tcl_NewListObj(4, fourl));
      }
    }

  Tcl_SetObjResult(interp, retl);

  free(res_array);
  free(avail_array);
  free(alloc_array);
  free(reser_array);
  free(down_array);

  return TCL_OK;
  }

int PBS_RescReserve(
    
  ClientData    clientData,
  Tcl_Interp   *interp,
  int           objc,
  Tcl_Obj *CONST objv[])

  {
  char *msg;
  int i, num, ret;
  Tcl_Obj **listp;
  char *cmd;
  char **res_array;
  resource_t resid;

  if (objc != 3)
    {
    Tcl_WrongNumArgs(interp, 1, objv,
                     "resource_id {resource1 resource2 ...}");
    return TCL_ERROR;
    }

  if (Tcl_GetIntFromObj(interp, objv[1], &resid) != TCL_OK)
    return TCL_ERROR;

  if ((ret = Tcl_ListObjGetElements(interp, objv[2],
                                    &num, &listp)) != TCL_OK)
    return ret;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (connector < 0)
    {
    log_err(-1, cmd, not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  res_array = (char **)malloc(sizeof(char *) * num);

  for (i = 0; i < num; i++)
    res_array[i] = Tcl_GetStringFromObj(listp[i], NULL);

  if (pbs_rescreserve(connector, res_array, num, &resid))
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s", msg ? msg : fail);
    log_err(-1, cmd, log_buffer);
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(resid));

  return TCL_OK;
  }

int PBS_RescRelease(
    
  ClientData    clientData,
  Tcl_Interp   *interp,
  int           objc,
  Tcl_Obj *CONST objv[])

  {
  char *msg;
  int ret;
  char *cmd;
  resource_t resid;

  if (objc != 2)
    {
    Tcl_WrongNumArgs(interp, 1, objv, "resource_id");
    return TCL_ERROR;
    }

  if (Tcl_GetIntFromObj(interp, objv[1], &resid) != TCL_OK)
    return TCL_ERROR;

  cmd = Tcl_GetStringFromObj(objv[0], NULL);

  if (connector < 0)
    {
    log_err(-1, cmd, not_connected);
    SET_PBSERR(PBSE_NOSERVER);
    return TCL_OK;
    }

  if ((ret = pbs_rescrelease(connector, resid)) != 0)
    {
    msg = pbs_geterrmsg(connector);
    sprintf(log_buffer, "%s", msg ? msg : fail);
    log_err(-1, cmd, log_buffer);
    }

  Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));

  return TCL_OK;
  }

int DateTime(

  ClientData      clientData,
  Tcl_Interp     *interp,
  int             argc,
  const char     *argv[])

  {
  time_t  when;

  struct tm tm, *t = NULL;
  int  i, yyy, len;
  char  rtime[64], hold[8];
  static const char *wkday[] = { "Sun", "Mon", "Tue", "Wed",
                           "Thu", "Fri", "Sat", NULL
                         };

  switch (argc)
    {

    case 1:   /* current date/time */
      when = time((time_t *)NULL);
      sprintf(log_buffer, "%ld", (long)when);
      Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
      return TCL_OK;

    case 2:
      strncpy(rtime, argv[1], sizeof(rtime));
      len = strlen(rtime);
      when = 0;

      if (len < 12)
        break;

      /* absolute date/time */
      for (i = 0; i < len; i++)
        {
        if (!isdigit(rtime[i]))
          break;
        }

      if (i != len || len > 14)
        {
        sprintf(log_buffer,
                "%s: bad absolute date format: %s",
                argv[0], rtime);
        Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
        return TCL_ERROR;
        }

      yyy = len - 10;

      for (i = 0; i < yyy; i++)
        hold[i] = rtime[i];

      hold[i] = '\0';

      tm.tm_year = atoi(hold);

      hold[0] = rtime[i++];

      hold[1] = rtime[i++];

      hold[2] = '\0';

      tm.tm_mon = atoi(hold) - 1;

      hold[0] = rtime[i++];

      hold[1] = rtime[i++];

      tm.tm_mday = atoi(hold);

      hold[0] = rtime[i++];

      hold[1] = rtime[i++];

      tm.tm_hour = atoi(hold);

      hold[0] = rtime[i++];

      hold[1] = rtime[i++];

      tm.tm_min = atoi(hold);

      hold[0] = rtime[i++];

      hold[1] = rtime[i];

      tm.tm_sec = atoi(hold);

      tm.tm_isdst = -1;

      when = mktime(&tm);

      if (when == -1)
        {
        sprintf(log_buffer,
                "%s: could not convert date: %s",
                argv[0], rtime);
        Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
        return TCL_ERROR;
        }

      Tcl_SetObjResult(interp, Tcl_NewLongObj((long)when));

      return TCL_OK;

    case 3:   /* relative weekday */

      for (i = 0; wkday[i]; i++)
        {
        if (strcmp(argv[1], wkday[i]) == 0)
          break;
        }

      if (wkday[i] == NULL)
        {
        sprintf(log_buffer,
                "%s: unrecognized weekday: %s",
                argv[0], argv[1]);
        Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
        return TCL_ERROR;
        }

      when = time((time_t *)NULL);

      t = localtime(&when);
      t->tm_mday += (i - t->tm_wday + 7) % 7;
      t->tm_hour = 0;
      t->tm_min = 0;
      t->tm_sec = 0;
      t->tm_isdst = -1;
      when = mktime(t);
      strncpy(rtime, argv[2], sizeof(rtime));
      len = strlen(rtime);
      break;

    default:
      sprintf(log_buffer,
              "%s: wrong # args: ?day? ?time?", argv[0]);
      Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
      return TCL_ERROR;
    }

  if (len != 8 || rtime[2] != ':' || rtime[5] != ':' ||
      !isdigit(rtime[0]) || !isdigit(rtime[1]) ||
      !isdigit(rtime[3]) || !isdigit(rtime[4]) ||
      !isdigit(rtime[6]) || !isdigit(rtime[7]))
    {
    sprintf(log_buffer,
            "%s: bad relative time format: %s", argv[0], rtime);
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  rtime[2] = rtime[5] = '\0';

  when += atoi(&rtime[0]) * 3600 +
          atoi(&rtime[3]) * 60 + atoi(&rtime[6]);

  Tcl_SetObjResult(interp, Tcl_NewLongObj((long)when));
  return TCL_OK;
  }

int StrFtime(
    
  ClientData    clientData,
  Tcl_Interp   *interp,
  int           objc,
  Tcl_Obj *CONST objv[])

  {

  struct tm *t;
  long  hold;
  time_t  when;

  if (objc != 3)
    {
    sprintf(log_buffer,
            "%s: wrong # args: format time",
            Tcl_GetStringFromObj(objv[0], NULL));
    Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
    return TCL_ERROR;
    }

  if (Tcl_GetLongFromObj(interp, objv[2], &hold) != TCL_OK)
    return TCL_ERROR;

  when = (time_t)hold;

  t = localtime(&when);

  (void)strftime(log_buffer, LOG_BUF_SIZE,
                 Tcl_GetStringFromObj(objv[1], NULL), t);

  Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);

  return TCL_OK;
  }

void add_cmds(

  Tcl_Interp *interp)

  {
  Tcl_CreateObjCommand(interp, "openrm", OpenRM, NULL, NULL);
  Tcl_CreateObjCommand(interp, "closerm", CloseRM, NULL, NULL);
  Tcl_CreateObjCommand(interp, "downrm", DownRM, NULL, NULL);
  Tcl_CreateObjCommand(interp, "configrm", ConfigRM, NULL, NULL);
  Tcl_CreateObjCommand(interp, "getreq", GetREQ, NULL, NULL);
  Tcl_CreateObjCommand(interp, "addreq", AddREQ, NULL, NULL);
  Tcl_CreateCommand(interp, "allreq", AllREQ, NULL, NULL);
  Tcl_CreateCommand(interp, "flushreq", FlushREQ, NULL, NULL);
  Tcl_CreateCommand(interp, "activereq", ActiveREQ, NULL, NULL);
  Tcl_CreateCommand(interp, "fullresp", FullResp, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsconnect", PBS_Connect, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsdisconnect", PBS_Disconnect, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsstatserv", PBS_StatServ, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsstatjob", PBS_StatJob, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsstatque", PBS_StatQue, NULL, NULL);
  Tcl_CreateObjCommand(interp, "pbsstatnode", PBS_StatNode, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsselstat", PBS_SelStat, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsrunjob", PBS_RunJob, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsmovejob", PBS_MoveJob, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsqenable", PBS_EnableQueue, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsqdisable", PBS_DisableQueue, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsqstart", PBS_StartQueue, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsqstop", PBS_StopQueue, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsasyrunjob", PBS_AsyRunJob, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsdeljob", PBS_DelJob, NULL, NULL);
  Tcl_CreateCommand(interp, "pbsholdjob", PBS_HoldJob, NULL, NULL);
  Tcl_CreateObjCommand(interp, "pbsalterjob", PBS_AlterJob, NULL, NULL);
  Tcl_CreateObjCommand(interp, "pbsrescquery", PBS_RescQuery, NULL, NULL);
  Tcl_CreateObjCommand(interp, "pbsrescreserve", PBS_RescReserve,
                       NULL, NULL);
  Tcl_CreateObjCommand(interp, "pbsrescrelease", PBS_RescRelease,
                       NULL, NULL);
  Tcl_CreateCommand(interp, "datetime", DateTime, NULL, NULL);
  Tcl_CreateObjCommand(interp, "strftime", StrFtime, NULL, NULL);

  /*
    * Extended scheduler commands from Univ. of Colorado
    */
  Tcl_CreateCommand(interp, "pbsrerunjob", PBS_ReRun, NULL, NULL);

  pbserr = Tcl_NewStringObj("pbs_errno", -1);

  Tcl_ObjSetVar2(interp, pbserr, NULL, \
                 Tcl_NewIntObj((0)), TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);

  site_cmds(interp);
  }
