/*
 * Copyright (C) 1994-2021 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of both the OpenPBS software ("OpenPBS")
 * and the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * OpenPBS is free software. You can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * OpenPBS is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Commercial License Information:
 *
 * PBS Pro is commercially licensed software that shares a common core with
 * the OpenPBS software.  For a copy of the commercial license terms and
 * conditions, go to: (http://www.pbspro.com/agreement.html) or contact the
 * Altair Legal Department.
 *
 * Altair's dual-license business model allows companies, individuals, and
 * organizations to create proprietary derivative works of OpenPBS and
 * distribute them - whether embedded or bundled with other software -
 * under a commercial license agreement.
 *
 * Use of Altair's trademarks, including but not limited to "PBS™",
 * "OpenPBS®", "PBS Professional®", and "PBS Pro™" and Altair's logos is
 * subject to Altair's trademark licensing policies.
 */

#include <pbs_config.h> /* the master config generated by configure */

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

#include "pbs_error.h"
#include "pbs_ifl.h"
#include "ifl_internal.h"
#include "log.h"
#include "resmon.h"
#include "rm.h"
#include "cmds.h"
#include "attribute.h"

char badparm[] = "%s: bad parameter";
char missingfd[] = "%s: missing file descriptor";
char not_connected[] = "not connected";
char fail[] = "failed";
#ifdef NAS /* localmod 071 */
char *tcl_atrsep = NULL;
#endif /* localmod 071 */
Tcl_Obj *pbserr;
Tcl_Obj *pbsmsg;

int connector = -1;
int (*local_disconnect)(int connection) = __pbs_disconnect;

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

#define SET_PBSMSG(msg)                             \
	(void) Tcl_ObjSetVar2(interp, pbsmsg, NULL, \
			      Tcl_NewStringObj((msg), -1), TCL_GLOBAL_ONLY)

#ifdef NAS
#define PBS_CALL(function)                                         \
	if (function) {                                            \
		Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));       \
		msg = pbs_geterrmsg(connector);                    \
		sprintf(log_buffer, "%s: %s (%d)", argv[1],        \
			msg ? msg : fail, pbs_errno);              \
		if (!quiet)                                        \
			log_err(-1, (char *) argv[0], log_buffer); \
	} else                                                     \
		Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
#else
#define PBS_CALL(function)                                   \
	if (function) {                                      \
		Tcl_SetObjResult(interp, Tcl_NewIntObj(-1)); \
		msg = pbs_geterrmsg(connector);              \
		sprintf(log_buffer, "%s: %s (%d)", argv[1],  \
			msg ? msg : fail, pbs_errno);        \
		log_err(-1, (char *) argv[0], log_buffer);   \
	} else                                               \
		Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
#endif

int
OpenRM(clientData, interp, objc, objv)
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);
#ifdef NAS
		if (!quiet)
#endif
			log_err(pbs_errno, Tcl_GetStringFromObj(objv[0], NULL), host);
	}

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

int
CloseRM(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	int fd, 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 = closerm(fd)) == -1) {
		Tcl_PosixError(interp);
#ifdef NAS
		if (!quiet)
#endif
			log_err(pbs_errno, cmd, Tcl_GetStringFromObj(objv[1], NULL));
	}

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

int
DownRM(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	int fd, 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 = downrm(fd)) == -1) {
		Tcl_PosixError(interp);
#ifdef NAS
		if (!quiet)
#endif
			log_err(pbs_errno, cmd, Tcl_GetStringFromObj(objv[1], NULL));
	}

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

int
ConfigRM(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	int fd, ret;
	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, filename);
	if (ret == -1) {
		Tcl_PosixError(interp);
#ifdef NAS
		if (!quiet)
#endif
			log_err(pbs_errno, cmd, filename);
	}

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

int
AddREQ(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	int fd, ret;
	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(fd, request);
	if (ret == -1) {
		Tcl_PosixError(interp);
#ifdef NAS
		if (!quiet)
#endif
			log_err(pbs_errno, cmd, request);
	}

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

int
AllREQ(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
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((char *) argv[1]);
	SET_PBSERR(pbs_errno);
	Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
	return TCL_OK;
}

int
GetREQ(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	int fd;
	char *ret, *getreq();
	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(fd)) == NULL) {
		if (pbs_errno) {
			Tcl_PosixError(interp);
#ifdef NAS
			if (!quiet)
#endif
				log_err(pbs_errno, cmd,
					Tcl_GetStringFromObj(objv[1], NULL));
		}
		SET_PBSERR(pbs_errno);
	} 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, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	if (argc != 1) {
		sprintf(log_buffer, badparm, (char *) argv[0]);
		Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
		return TCL_ERROR;
	}

	flushreq();

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
ActiveREQ(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
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);
#ifdef NAS
		if (!quiet)
#endif
			log_err(pbs_errno, (char *) argv[0], log_buffer);
	}

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

int
FullResp(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
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, (char *) argv[1], &flag) != TCL_OK)
		return TCL_ERROR;

	fullresp(flag);
	SET_PBSERR(0);
	return TCL_OK;
}

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

	if (argc == 2)
		server = 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;
	}

	local_disconnect(connector);
	pbs_errno = PBSE_NONE;
	if ((connector = pbs_connect(server)) < 0) {
		Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
		sprintf(log_buffer, "%s (%d)",
			server ? server : "DefaultServer",
			pbs_errno);
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], log_buffer);
	} else
		Tcl_SetObjResult(interp, Tcl_NewIntObj(0));

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

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

	pbs_errno = PBSE_NONE;
	local_disconnect(connector);
	Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
	connector = -1;

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

Tcl_Obj *
attrlist(interp, ap)
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],
#ifdef NAS /* localmod 071 */
					       tcl_atrsep, ap->resource, NULL);
#else
					       TCL_ATRSEP, ap->resource, NULL);
#endif /* localmod 071 */
		}
		twol[1] = Tcl_NewStringObj(ap->value, -1);
		Tcl_ListObjAppendElement(interp, ret, Tcl_NewListObj(2, twol));

		ap = ap->next;
	}
	return (ret);
}

void
	batresult(interp, bs)
		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, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	if ((bs = pbs_statserver(connector, NULL, NULL)) == NULL) {
		if (pbs_errno != PBSE_NONE) {
			msg = pbs_geterrmsg(connector);
			sprintf(log_buffer, "%s (%d)",
				msg ? msg : fail, pbs_errno);
#ifdef NAS
			if (!quiet)
#endif
				log_err(-1, (char *) 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);
	}

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_StatJob(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	struct batch_status *bs;
	char *extend = NULL;

	if (argc > 2) { /* can have one argument for extend field */
		sprintf(log_buffer, badparm, argv[0]);
		Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
		return TCL_ERROR;
	}
	if (argc == 2) {
		extend = (char *) argv[1];
	}

	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	if ((bs = pbs_statjob(connector, NULL, NULL, extend)) == NULL) {
		if (pbs_errno != PBSE_NONE) {
			msg = pbs_geterrmsg(connector);
			sprintf(log_buffer, "%s (%d)",
				msg ? msg : fail, pbs_errno);
#ifdef NAS
			if (!quiet)
#endif
				log_err(-1, (char *) argv[0], log_buffer);
		}
	} else
		batresult(interp, bs);

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_SelStat(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	struct batch_status *bs;

	static struct attropl att1 = {
		NULL,
		"queue_type",
		NULL,
		"E",
		EQ};
	static struct attropl att2 = {
		&att1,
		"job_state",
		NULL,
		"Q",
		EQ};

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

	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	if ((bs = pbs_selstat(connector, &att2, NULL, NULL)) == NULL) {
		if (pbs_errno != PBSE_NONE) {
			msg = pbs_geterrmsg(connector);
			sprintf(log_buffer, "%s (%d)",
				msg ? msg : fail, pbs_errno);
#ifdef NAS
			if (!quiet)
#endif
				log_err(-1, (char *) argv[0], log_buffer);
		}
	} else
		batresult(interp, bs);

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_StatQue(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	if ((bs = pbs_statque(connector, NULL, NULL, NULL)) == NULL) {
		if (pbs_errno != PBSE_NONE) {
			msg = pbs_geterrmsg(connector);
			sprintf(log_buffer, "%s (%d)",
				msg ? msg : fail, pbs_errno);
#ifdef NAS
			if (!quiet)
#endif
				log_err(-1, (char *) argv[0], log_buffer);
		}
	} else
		batresult(interp, bs);

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_StatNode(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	char *msg, *cmd;
	char *node = NULL;
	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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, cmd, not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	if ((bs = pbs_statnode(connector, node, NULL, NULL)) == NULL) {
		if (pbs_errno != PBSE_NONE) {
			msg = pbs_geterrmsg(connector);
			sprintf(log_buffer, "%s (%d)",
				msg ? msg : fail, pbs_errno);
#ifdef NAS
			if (!quiet)
#endif
				log_err(-1, cmd, log_buffer);
		}
	} else
		batresult(interp, bs);

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_AsyRunJob(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	char *location = NULL;

	if (argc == 3)
		location = (char *) 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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	PBS_CALL(pbs_asyrunjob(connector, (char *) argv[1], location, NULL))

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_RunJob(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	char *location = NULL;

	if (argc == 3)
		location = (char *) 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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	PBS_CALL(pbs_runjob(connector, (char *) argv[1], location, NULL))

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_ReRun(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	char *extend = "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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	PBS_CALL(pbs_rerunjob(connector, (char *) argv[1], extend))

	SET_PBSERR(pbs_errno);

	return TCL_OK;
}

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

	if (argc == 3)
		location = (char *) 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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

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

	PBS_CALL(pbs_movejob(connector, job_id_out, location, NULL))

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_DelJob(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	char *msg;
	char *message = NULL;

	if (argc == 3)
		message = (char *) 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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	PBS_CALL(pbs_deljob(connector, (char *) argv[1], message))

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

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

	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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	PBS_CALL(pbs_holdjob(connector, (char *) argv[1], SYSTEM_HOLD, NULL))

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_QueueOp(clientData, interp, argc, argv, attr)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
struct attropl *attr;
{
	int merr;

	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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, (char *) argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	merr = pbs_manager(connector, MGR_CMD_SET, MGR_OBJ_QUEUE,
			   (char *) argv[1], attr, NULL);
	if (merr != 0) {
		sprintf(log_buffer, "%s: %s %s", argv[0], argv[1],
			pbs_geterrmsg(connector));
		Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
		return TCL_ERROR;
	}

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

int
PBS_EnableQueue(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	static struct attropl attr = {NULL, "enabled", NULL, "TRUE", SET};
	return PBS_QueueOp(clientData, interp, argc, argv, &attr);
}

int
PBS_DisableQueue(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	static struct attropl attr = {NULL, "enabled", NULL, "FALSE", SET};
	return PBS_QueueOp(clientData, interp, argc, argv, &attr);
}

int
PBS_StartQueue(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	static struct attropl attr = {NULL, "started", NULL, "TRUE", SET};
	return PBS_QueueOp(clientData, interp, argc, argv, &attr);
}

int
PBS_StopQueue(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	static struct attropl attr = {NULL, "started", NULL, "FALSE", SET};
	return PBS_QueueOp(clientData, interp, argc, argv, &attr);
}

int
PBS_AlterJob(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	static char id[] = "PBS_AlterJob";
	char *msg;
	int i, num, tre, ret;
	Tcl_Obj **listp, **indp;
	struct attrl *attrs, *atp = NULL;
	char *cmd, *jobid;

	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 = new_attrl();
		if (atp == NULL) {
			sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
			if (!quiet)
#endif
				log_err(errno, id, log_buffer);
			return TCL_ERROR;
		}
		if ((atp->name = strdup(Tcl_GetStringFromObj(indp[0], NULL))) == NULL) {
			sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
			if (!quiet)
#endif
				log_err(errno, id, log_buffer);
			free(atp);
			return TCL_ERROR;
		}
		if ((atp->resource = strdup(Tcl_GetStringFromObj(indp[1], NULL))) == NULL) {
			sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
			if (!quiet)
#endif
				log_err(errno, id, log_buffer);
			free(atp->name);
			free(atp);
			return TCL_ERROR;
		}
		if ((atp->value = strdup(Tcl_GetStringFromObj(indp[2], NULL))) == NULL) {
			sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
			if (!quiet)
#endif
				log_err(errno, id, log_buffer);
			free(atp->resource);
			free(atp->name);
			free(atp);
			return TCL_ERROR;
		}
		atp->next = attrs;
		attrs = atp;
	}

	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, cmd, not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		goto done;
	}

	jobid = Tcl_GetStringFromObj(objv[1], NULL);
	if (pbs_alterjob(connector, jobid, atp, NULL)) {
		Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
		msg = pbs_geterrmsg(connector);
		sprintf(log_buffer, "%s: %s (%d)", jobid,
			msg ? msg : fail, pbs_errno);
#ifdef NAS
		if (!quiet)
#endif
			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);
	}

	SET_PBSERR(pbs_errno);
	return ret;
}

int
PBS_RescQuery(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	static char id[] = "PBS_RescQuery";
	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;
	}

	cmd = Tcl_GetStringFromObj(objv[0], NULL);
	if ((ret = Tcl_ListObjGetElements(interp, objv[1],
					  &num, &listp)) != TCL_OK)
		return ret;
	if (num == 0) {
		sprintf(log_buffer, "%s: null resource list", cmd);
		Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
		return TCL_ERROR;
	}

	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, cmd, not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	res_array = (char **) malloc(sizeof(char *) * num);
	if (res_array == NULL) {
		sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
		if (!quiet)
#endif
			log_err(errno, id, log_buffer);
		return TCL_ERROR;
	}
	avail_array = (int *) malloc(sizeof(int) * num);
	if (avail_array == NULL) {
		sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
		if (!quiet)
#endif
			log_err(errno, id, log_buffer);
		free(res_array);
		return TCL_ERROR;
	}
	alloc_array = (int *) malloc(sizeof(int) * num);
	if (alloc_array == NULL) {
		sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
		if (!quiet)
#endif
			log_err(errno, id, log_buffer);
		free(res_array);
		free(avail_array);
		return TCL_ERROR;
	}
	reser_array = (int *) malloc(sizeof(int) * num);
	if (reser_array == NULL) {
		sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
		if (!quiet)
#endif
			log_err(errno, id, log_buffer);
		free(res_array);
		free(avail_array);
		free(alloc_array);
		return TCL_ERROR;
	}
	down_array = (int *) malloc(sizeof(int) * num);
	if (down_array == NULL) {
		sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
		if (!quiet)
#endif
			log_err(errno, id, log_buffer);
		free(res_array);
		free(avail_array);
		free(alloc_array);
		free(reser_array);
		return TCL_ERROR;
	}
	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 (%d)", msg ? msg : fail, pbs_errno);
#ifdef NAS
		if (!quiet)
#endif
			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);

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_RescReserve(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	static char id[] = "PBS_RescReserve";
	char *msg;
	int i, num, ret;
	Tcl_Obj **listp;
	char *cmd;
	char **res_array;
	pbs_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;

	cmd = Tcl_GetStringFromObj(objv[0], NULL);
	if ((ret = Tcl_ListObjGetElements(interp, objv[2],
					  &num, &listp)) != TCL_OK)
		return ret;
	if (num == 0) {
		sprintf(log_buffer, "%s: null resource list", cmd);
		Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
		return TCL_ERROR;
	}

	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, cmd, not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}

	res_array = (char **) malloc(sizeof(char *) * num);
	if (res_array == NULL) {
		sprintf(log_buffer, "Unable to allocate memory (malloc error)");
#ifdef NAS
		if (!quiet)
#endif
			log_err(errno, id, log_buffer);
		return TCL_ERROR;
	}
	for (i = 0; i < num; i++)
		res_array[i] = Tcl_GetStringFromObj(listp[i], NULL);

	pbs_errno = 0;
	if (pbs_rescreserve(connector, res_array, num, &resid) != 0) {
		msg = pbs_geterrmsg(connector);
		sprintf(log_buffer, "%s (%d)", msg ? msg : fail, pbs_errno);
		SET_PBSMSG(log_buffer);
	}
	Tcl_SetObjResult(interp, Tcl_NewIntObj(resid));

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_RescRelease(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	char *msg;
	int ret;
	char *cmd;
	pbs_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) {
#ifdef NAS
		if (!quiet)
#endif
			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 (%d)", msg ? msg : fail, pbs_errno);
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, cmd, log_buffer);
	}
	Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));

	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_ResvStatus(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *CONST argv[];
{
	char *msg;
	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) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}
	if ((bs = pbs_statresv(connector, NULL, NULL, NULL)) == NULL) {
		if (pbs_errno != PBSE_NONE) {
			msg = pbs_geterrmsg(connector);
			sprintf(log_buffer, "%s (%d)",
				msg ? msg : fail, pbs_errno);
#ifdef NAS
			if (!quiet)
#endif
				log_err(-1, argv[0], log_buffer);
		}
	} else
		batresult(interp, bs);
	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_ResvConfirm(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *CONST argv[];
{
	char *msg = NULL;
	unsigned long stime = 0;

	if (argc < 2 || argc > 4) {
		sprintf(log_buffer,
			"%s: wrong # args: resv_id vnodes ?stime? ?reason?",
			argv[0]);
		Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
		return TCL_ERROR;
	}
	if (argc == 4) {
		msg = argv[3];
	}
	if (argc == 3) {
		stime = strtoul(argv[2], NULL, 10);
	}
	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}
	PBS_CALL(pbs_confirmresv(connector, argv[1], argv[2], stime, msg))
	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
PBS_ResvDelete(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *CONST argv[];
{
	char *msg = NULL;

	if (argc < 2 || argc > 3) {
		sprintf(log_buffer,
			"%s: wrong # args: resv_id ?reason?", argv[0]);
		Tcl_SetResult(interp, log_buffer, TCL_VOLATILE);
		return TCL_ERROR;
	}
	if (argc == 3)
		msg = argv[2];
	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, argv[0], not_connected);
		SET_PBSERR(PBSE_NOSERVER);
		return TCL_OK;
	}
	PBS_CALL(pbs_delresv(connector, argv[1], msg))
	SET_PBSERR(pbs_errno);
	return TCL_OK;
}

int
LogMsg(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	char *tag = NULL;
	char *msg = NULL;

	if (objc != 3) {
		Tcl_WrongNumArgs(interp, 1, objv, "tag message");
		return TCL_ERROR;
	} else {
		tag = Tcl_GetStringFromObj(objv[1], NULL);
		msg = Tcl_GetStringFromObj(objv[2], NULL);
	}

	if (connector < 0) {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, tag, not_connected);
		Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
	} else {
#ifdef NAS
		if (!quiet)
#endif
			log_err(-1, tag, msg);
		Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
	}

	return TCL_OK;
}

int
DateTime(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
	time_t when;
	struct tm tm, *t = NULL;
	int i, yyy, len;
	char rtime[64], hold[8];
	static char *wkday[] = {"Sun", "Mon", "Tue", "Wed",
				"Thu", "Fri", "Sat", NULL};

	switch (argc) {

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

		case 2:
			snprintf(rtime, sizeof(rtime), "%s", argv[1]);
			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(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);
			snprintf(rtime, sizeof(rtime), "%s", argv[2]);
			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, interp, objc, objv)
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;
}

int
PBS_PbsPortInfoCmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
	int index, result;
	static const char *subCmds[] = {
		"batch_service_port", "batch_service_port_dis",
		"mom_service_port", "manager_service_port",
		NULL};
	enum ISubCmdIdx {
		IBatchSvcIdx,
		IBatchSvcDisIdx,
		IMomSvcIdx,
		IManSvcIdx
	};

	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 1, objv, "batch_service_port|batch_service_port_dis|mom_service_port|manager_service_port");
		return TCL_ERROR;
	}

	result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", 0, &index);
	if (result != TCL_OK) {
		return result;
	}

	switch (index) {
		case IBatchSvcIdx:
			result = pbs_conf.batch_service_port;
			break;
		case IBatchSvcDisIdx:
			result = pbs_conf.batch_service_port_dis;
			break;
		case IMomSvcIdx:
			result = pbs_conf.mom_service_port;
			break;
		case IManSvcIdx:
			result = pbs_conf.manager_service_port;
			break;
	}

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

void
	add_cmds(interp)
		Tcl_Interp *interp;
{
	extern void site_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_CreateObjCommand(interp, "pbsportinfo", PBS_PbsPortInfoCmd,
			     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, "pbsresvstat", PBS_ResvStatus, NULL, NULL);
	Tcl_CreateCommand(interp, "pbsresvconf", PBS_ResvConfirm, NULL, NULL);
	Tcl_CreateCommand(interp, "pbsresvdel", PBS_ResvDelete, NULL, NULL);
	Tcl_CreateObjCommand(interp, "logmsg", LogMsg, 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);

	/*
	 * Initialize global variables pbs_errno and pbs_errmsg
	 */
	pbserr = Tcl_NewStringObj("pbs_errno", -1);
	Tcl_ObjSetVar2(interp, pbserr, NULL, Tcl_NewIntObj((0)),
		       TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);
	pbsmsg = Tcl_NewStringObj("pbs_errmsg", -1);
	Tcl_ObjSetVar2(interp, pbsmsg, NULL, Tcl_NewStringObj("no msg", -1),
		       TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);

#ifdef NAS /* localmod 071 */
	if (tcl_atrsep)
		free(tcl_atrsep);
	tcl_atrsep = strdup(TCL_ATRSEP);
#endif /* localmod 071 */
	site_cmds(interp);
}
