/*
 * Copyright (C) 1994-2018 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * PBS Pro 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.
 *
 * PBS Pro 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:
 *
 * For a copy of the commercial license terms and conditions,
 * go to: (http://www.pbspro.com/UserArea/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 PBS Pro 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™",
 * "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
 * trademark licensing policies.
 *
 */

/**
 * @file    pbs_sched_win.c
 *
 * @brief
 * 		pbs_sched_win.c - contains functions related to Pbs scheduler for Windows.
 *
 * Functions included are:
 *	on_segv()
 *	die()
 *	server_disconnect()
 *	socket_to_conn()
 *	addclient()
 *	read_config()
 *	restart()
 *	badconn()
 *	server_command()
 *	update_svr_schedobj()
 *	lock_out()
 *	are_we_primary()
 *	usage2()
 *	log_rppfail()
 *	log_tppmsg()
 *	main()
 *
 */
#include <pbs_config.h>   /* the master config generated by configure */

#ifdef WIN32
#include        <direct.h>
#include        <stdlib.h>
#include        <stdio.h>
#include        <stdlib.h>
#include        <string.h>
#include        <signal.h>
#include        <time.h>
#include        <ctype.h>
#include        <limits.h>
#include        <fcntl.h>
#include        <errno.h>
#include        <sys/types.h>
#include        <time.h>
#include        <sys/stat.h>
#include        <io.h>
#include        <process.h>
#include        "win.h"
#else
#include        <unistd.h>
#include	<stdio.h>
#include        <stdlib.h>
#include        <string.h>
#include        <signal.h>
#include	<time.h>
#include        <ctype.h>
#include        <limits.h>
#include        <fcntl.h>
#include	<errno.h>
#include	<netdb.h>

#include        <sys/types.h>
#include	<sys/wait.h>
#include	<sys/time.h>
#include        <sys/stat.h>
#include        <sys/socket.h>
#include	<sys/resource.h>
#include        <netinet/in.h>
#include        <arpa/inet.h>
#ifdef _POSIX_MEMLOCK
#include	<sys/mman.h>
#endif	/* _POSIX_MEMLOCK */
#endif
#include        <libutil.h>

#if defined(FD_SET_IN_SYS_SELECT_H)
#include <sys/select.h>
#endif

#include	"pbs_version.h"
#include	"portability.h"
#include	"libpbs.h"
#include	"pbs_error.h"
#include	"pbs_ifl.h"
#include	"pbs_ecl.h"
#include	"log.h"
#include	"sched_cmds.h"
#include	"server_limits.h"
#include	"net_connect.h"
#include	"rm.h"
#include	"rpp.h"
#include	"pbs_internal.h"
#include	"pbs_share.h"
#include	"config.h"
#include	"fifo.h"
#include	"globals.h"


struct		connect_handle connection[NCONNECTS];
int		pbs_errno;
int		connector;
int		server_sock;
int		second_connection = -1;
struct tpp_config tpp_conf; /* global settings for tcp */

#define		START_CLIENTS	2	/* minimum number of clients */
#define		MAX_PORT_NUM 65535
#define		STARTING_PORT_NUM 15050
pbs_net_t	*okclients = NULL;	/* accept connections from */
int		numclients = 0;		/* the number of clients */
char		*configfile = NULL;	/* name of file containing
						 client names to be added */

char		*oldpath;
char		**glob_argv;
char		usage[] =
	"[-d home][-L logfile][-p file][-I schedname][-S port][-R port][-n][-c clientsfile]";
struct	sockaddr_in	saddr;
extern char	*msg_noloopbackstopdaemon;
#ifndef WIN32
sigset_t	allsigs;
#endif

int 		pbs_rm_port;
int		got_sigpipe = 0;   /* needed for UNIX so we need the symbol */

int	schedreq();

#ifdef WIN32
void WINAPI PbsSchedMain(DWORD dwArgc, LPTSTR *rgszArgv);
void WINAPI PbsSchedHandler(DWORD dwControl);
DWORD WINAPI main_thread(void *pv);

// NOTE: Note the global state used by your service. Your service has a name,
// state and a status handle used by SetServiceStatus.
//
int stalone = 0;
const TCHAR * const     g_PbsSchedName = __TEXT("pbs_sched");
HANDLE                  g_hthreadMain = 0;
SERVICE_STATUS_HANDLE   g_ssHandle = 0;
DWORD                   g_dwCurrentState = SERVICE_START_PENDING;
#endif

extern char *msg_startup1;

#ifndef WIN32
/* used in segv restart */
time_t segv_start_time;
time_t segv_last_time;

int	schedreq();

/**
 * @brief
 * 		cleanup after a segv and re-exec.  Trust as little global mem
 * 		as possible... we don't know if it could be corrupt
 *
 * @param[in]	sig	-	signal
 */
void
on_segv(int sig)
{

	/* we crashed less then 5 minutes ago, lets not restart ourself */
	if ((segv_last_time - segv_start_time) < 300) {
		log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO, "on_segv", "received a sigsegv within 5 minutes of start: aborting.");
		abort();
	}

	log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO, "on_segv", "received segv and restarting");

	if (fork() > 0) { /* the parent rexec's itself */
		sleep(10);		/* allow the child to die */
		execv(glob_argv[0], glob_argv);
		exit(3);
	}
	else
		abort();			/* allow to core and exit */
}
#endif

/**
 * @brief
 *      Clean up after a signal.
 *
 * @param[in]	sig	-	signal
 */
void
die(int sig)
{
	if (sig > 0) {
		sprintf(log_buffer, "caught signal %d", sig);
		log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO,
			__func__, log_buffer);
	}
	else {
		log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO,
			__func__, "abnormal termination");
	}

	log_close(1);
	exit(1);
}

/**
 *	@brief
 *		specialized disconnect function similar to pbs_disconnect()
 *		We need a specialized function to disconnect the socket
 *		between the server and scheduler.  If we use the regular one,
 *		we can have timeout problems and run out of reserved ports
 *
 *	@param[in]	connect	-	connection descriptor to disconnect
 *
 *	@return	success/failure
 *	@retval	1	: successfully disconnected
 *	@retval 0	: failure to disconnect
 */
int
server_disconnect(int connect)
{
	if (connect < 0)
		return;

	if (pbs_client_thread_lock_connection(connect) != 0)
		return;

#ifdef WIN32
	closesocket(connection[connect].ch_socket);
#else
	close(connection[connect].ch_socket);
#endif

	if (connection[connect].ch_errtxt != NULL)
		free(connection[connect].ch_errtxt);

	connection[connect].ch_errno = 0;
	connection[connect].ch_inuse = 0;

	(void)pbs_client_thread_unlock_connection(connect);
	pbs_client_thread_destroy_connect_context(connect);

	return;
}

/**
 * @brief
 * 		assign socket to the connect handle and unlock the connectable thread.
 *
 * @param[in]	sock	-	opened socket
 *
 * @return	int
 * @return	index,i	: if thread is unlocked
 * @retval	-1	: error, not able to connect.
 */
int
socket_to_conn(int sock)
{
	int     i;

	for (i=0; i<NCONNECTS; i++) {
		if (connection[i].ch_inuse == 0) {

			if (pbs_client_thread_lock_conntable() != 0)
				return -1;

			connection[i].ch_inuse = 1;
			connection[i].ch_errno = 0;
			connection[i].ch_socket= sock;
			connection[i].ch_errtxt = NULL;

			if (pbs_client_thread_unlock_conntable() != 0)
				return -1;

			return (i);
		}
	}
	pbs_errno = PBSE_NOCONNECTS;
	return (-1);
}

/**
 * @brief
 * 		add a new client to the list of clients.
 *
 * @param[in]	name	-	Client name.
 */
int
addclient(char *name)
{
	int	i;
	struct	hostent		*host;
#ifndef WIN32
	struct	hostent		*gethostbyname();
#endif
	struct  in_addr saddr;

	if ((host = gethostbyname(name)) == NULL) {
		sprintf(log_buffer, "host %s not found", name);
		log_err(-1, __func__, log_buffer);
		return -1;
	}

	for (i = 0; host->h_addr_list[i]; i++) {
		if (numclients >= START_CLIENTS) {
			pbs_net_t	*newclients;

			newclients = realloc(okclients,
				sizeof(pbs_net_t)*(numclients+1));
			if (newclients == NULL)
				return -1;
			okclients = newclients;
		}
		memcpy((char *)&saddr, host->h_addr_list[i], host->h_length);
		okclients[numclients++] = saddr.s_addr;
	}

	return 0;
}

/**
 * @brief
 * 		read_config - read and process the configuration file (see -c option)
 * @par
 *		Currently, the only statement is $clienthost to specify which systems
 *		can contact the scheduler.
 *
 * @param[in]	file	-	configuration file
 *
 * @return	int
 * @retval	0	: Ok
 * @retval	-1	: !nOtOk!
 */
#define CONF_LINE_LEN 120

static
int
read_config(char *file)
{
	FILE	*conf;
	int	i;
	char	line[CONF_LINE_LEN];
	char	*token;
	struct	specialconfig {
		char	*name;
		int	(*handler)();
	} special[] = {
		{"clienthost",	addclient },
		{ NULL,		NULL }
	};


#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
#ifdef WIN32
	if (chk_file_sec(file, 0, 0, WRITES_MASK, 0))
#else
	if (chk_file_sec(file, 0, 0, S_IWGRP|S_IWOTH, 1))
#endif
		return (-1);
#endif

	if ((conf = fopen(file, "r")) == NULL) {
		log_err(errno, __func__, "cannot open config file");
		return (-1);
	}
	while (fgets(line, CONF_LINE_LEN, conf)) {

		if ((line[0] == '#') || (line[0] == '\n'))
			continue;		/* ignore comment & null line */
		else if (line[0] == '$') {	/* special */

			if ((token = strtok(line, " \t")) == NULL)
				token = "";
			for (i=0; special[i].name; i++) {
				if (strcmp(token+1, special[i].name) == 0)
					break;
			}
			if (special[i].name == NULL) {
				sprintf(log_buffer, "config name %s not known",
					token);
				log_record(PBSEVENT_ERROR,
					PBS_EVENTCLASS_SERVER, LOG_INFO,
					msg_daemonname, log_buffer);
				continue;
			}
			token = strtok(NULL, " \t");
			if (*(token+strlen(token)-1) == '\n')
				*(token+strlen(token)-1) = '\0';

			if (special[i].handler(token)) {
				fclose(conf);
				return (-1);
			}

		} else {
			log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_SERVER,
				LOG_INFO, msg_daemonname,
				"invalid line in config file");
			fclose(conf);
			return (-1);
		}
	}
	fclose(conf);
	return (0);
}

/**
 * @brief
 * 		restart on signal
 *
 * @param[in]	sig	-	signal
 */
void
restart(int sig)
{
	if (sig) {
		log_close(1);
		log_open(logfile, path_log);
		sprintf(log_buffer, "restart on signal %d", sig);
	} else {
		sprintf(log_buffer, "restart command");
	}
	log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__,
		log_buffer);
	if (configfile) {
		if (read_config(configfile) != 0)
			die(0);
	}
	schedule(SCH_CONFIGURE, -1, NULL);
}

/**
 * @brief
 * 		log the bad connection message
 *
 * @param[in]	msg	-	The message to be logged.
 */
void
badconn(char *msg)
{
	struct	in_addr	addr;
	char		buf[5*sizeof(addr) + 100];
	struct	hostent	*phe;

	addr = saddr.sin_addr;
	phe = gethostbyaddr((void *)&addr, sizeof(addr), AF_INET);
	if (phe == NULL) {
		char	hold[6];
		int	i;
		union {
			struct	in_addr aa;
			u_char		bb[sizeof(addr)];
		} uu;

		uu.aa = addr;
		sprintf(buf, "%u", uu.bb[0]);
		for (i=1; i<sizeof(addr); i++) {
			sprintf(hold, ".%u", uu.bb[i]);
			strcat(buf, hold);
		}
	}
	else {
		strncpy(buf, phe->h_name, sizeof(buf));
		buf[sizeof(buf)-1] = '\0';
	}

	sprintf(log_buffer, "%s on port %u %s",
		buf, ntohs(saddr.sin_port), msg);
	log_err(-1, __func__, log_buffer);
	return;
}

/**
 *
 * @brief
 *		Gets a scheduling command  from the server from the primary
 *		socket connection to the server. This would also attempt to
 *		get a secondary socket connection to the server, which will
 *		contain high priority scheduling commands like
 *		SCH_SCHEDULER_RESTART_CYCLE.
 *
 * @param[in]	jid	-	if command received is SCH_SCHEDULE_AJOB, then
 *			  			*jid will hold the jobid.
 *
 * @return	int
 * @retval	SCH_ERROR	-	if an error occured.
 * @reval	<scheduling command>	-	for example, SCH_SCHEDULE_CMD>
 *
 * @note
 *		The returned *jid is a malloc-ed string which must be freed by the
 *		caller.
 */
int
server_command(char **jid)
{
	int		new_socket;
	int		slen;
	int		i = 0;
	unsigned int	cmd;
	pbs_net_t	addr;
	extern	int	get_sched_cmd(int sock, int *val, char **jobid);
	fd_set		fdset;
	struct timeval  timeout;

	slen = sizeof(saddr);
	new_socket = accept(server_sock,
		(struct sockaddr *)&saddr, &slen);
	if (new_socket == -1) {
		log_err(errno, __func__, "accept");
		return SCH_ERROR;
	}

	if (set_nodelay(new_socket) == -1) {
#ifdef WIN32
		errno = WSAGetLastError();
#endif
		snprintf(log_buffer, sizeof(log_buffer), "cannot set nodelay on primary socket connection %d (errno=%d)\n", new_socket, errno);
		log_err(-1, __func__, log_buffer);
		return SCH_ERROR;
	}

	if (ntohs(saddr.sin_port) >= IPPORT_RESERVED) {
		badconn("non-reserved port");
#ifdef WIN32
		closesocket(new_socket);
#else
		close(new_socket);
#endif
		return SCH_ERROR;
	}
	addr = (pbs_net_t)saddr.sin_addr.s_addr;
	for (i=0; i<numclients; i++) {
		if (addr == okclients[i])
			break;
	}
	if (i == numclients) {
		badconn("unauthorized host");
#ifdef WIN32
		closesocket(new_socket);
#else
		close(new_socket);
#endif
		return SCH_ERROR;
	}

	if ((connector = socket_to_conn(new_socket)) < 0) {
		log_err(errno, __func__, "socket_to_conn");
#ifdef WIN32
		closesocket(new_socket);
#else
		close(new_socket);
#endif
		return SCH_ERROR;
	}

	/* get_sched_cmd() located in file get_4byte.c */
	if (get_sched_cmd(new_socket, &cmd, jid) != 1) {
		log_err(errno, __func__, "get_sched_cmd");
#ifdef WIN32
		closesocket(new_socket);
#else
		close(new_socket);
#endif
		return SCH_ERROR;
	}

	/* Obtain the second server socket connnection		*/
	/* this second connection is for server to communicate	*/
	/* "super" high priority command like			*/
	/* SCH_SCHEDULE_RESTART_CYCLE				*/
	/* This won't cause scheduling to quit if an error      */
	/* resulted in obtaining this second connection.	*/
	timeout.tv_usec = 0;
	timeout.tv_sec  = 1;

	FD_ZERO(&fdset);
	FD_SET(server_sock, &fdset);
	if ((select(FD_SETSIZE, &fdset, NULL, NULL,
		&timeout) != -1)  && (FD_ISSET(server_sock, &fdset))) {
		int	cmd2;
		char	*jid2 = NULL;

		second_connection = accept(server_sock,
			(struct sockaddr *)&saddr, &slen);
		if (second_connection == -1) {
			log_err(errno, __func__,
				"warning: failed to get second_connection");
			return cmd; /* bail out early */
		}

		if (set_nodelay(second_connection) == -1) {
#ifdef WIN32
			errno = WSAGetLastError();
#endif
			snprintf(log_buffer, sizeof(log_buffer), "cannot set nodelay on secondary socket connection %d (errno=%d)\n", second_connection, errno);
			log_err(-1, __func__, log_buffer);
			return cmd;
		}

		if (ntohs(saddr.sin_port) >= IPPORT_RESERVED) {
			badconn("second_connection: non-reserved port");
#ifdef WIN32
			(void)closesocket(second_connection);
#else
			close(second_connection);
#endif
			second_connection = -1;
			return cmd;
		}

		addr = (pbs_net_t)saddr.sin_addr.s_addr;
		for (i=0; i<numclients; i++) {
			if (addr == okclients[i])
				break;
		}

		if (i == numclients) {
			badconn("second_connection: unauthorized host");
#ifdef WIN32
			(void)closesocket(second_connection);
#else
			close(second_connection);
#endif
			second_connection = -1;
			return cmd;
		}


		if (get_sched_cmd(second_connection, &cmd2, &jid2) != 1) {
			log_err(errno, __func__, "get_sched_cmd");
#ifdef WIN32
			(void)closesocket(second_connection);
#else
			close(second_connection);
#endif
			second_connection = -1;
		}

		if (jid2 != NULL) {
			free(jid2);
		}
	} else {

		log_event(PBSEVENT_DEBUG, LOG_DEBUG,
			PBS_EVENTCLASS_SERVER, __func__,
			"warning: timed-out getting second_connection");
	}

	return cmd;
}


#ifdef WIN32

/**
 * @brief
 * 		lock_out - lock out other daemons from this directory.
 *
 * @param[in]	fds	-	file descriptor
 * @param[in]	op	-	F_WRLCK  or  F_UNLCK
 *
 * @return	1
 */

static void
lock_out(int fds, int op)
{
	char	     buf[100];
	struct stat  sbuf;

	if (fstat(fds, &sbuf) == -1) {
		log_err(errno, "lock_out", "can't stat lock file");
		exit(1);
	}

	if (_locking(fds, op, (long)sbuf.st_size) != -1) {
		if (op == F_WRLCK) {
			/* if write-lock, record pid in file */
			(void)sprintf(buf, "%d\n", getpid());
			(void)write(fds, buf, strlen(buf));
		}
		return;
	}

	if (op == F_WRLCK) {
		(void)strcpy(log_buffer,
			"pbs_sched: another scheduler running\n");
		log_err(errno, msg_daemonname, log_buffer);
		fprintf(stderr, log_buffer);
		exit(1);
	}
}

#else
/**
 * @brief
 * 		lock_out - lock out other daemons from this directory.
 *
 * @param[in]	fds	-	file descriptor
 * @param[in]	op	-	F_WRLCK  or  F_UNLCK
 *
 * @return	1
 */

static void
lock_out(int fds, int op)
{
	struct flock flock;

	flock.l_type   = op;
	flock.l_whence = SEEK_SET;
	flock.l_start  = 0;
	flock.l_len    = 0;	/* whole file */
	if (fcntl(fds, F_SETLK, &flock) < 0) {
		(void)strcpy(log_buffer, "pbs_sched: another scheduler running\n");
		log_err(errno, msg_daemonname, log_buffer);
		fprintf(stderr, log_buffer);
		exit(1);
	}
}
#endif

/**
 * @brief
 * 		are_we_primary - are we on the primary Server host
 *		If either the only configured Server or the Primary in a failover
 *		configuration - return true
 *
 * @return	int
 * @retval	0	: we are the secondary
 * @retval	-1	: cannot be neither
 * @retval	1	: we are the listed primary
 */
static int
are_we_primary()
{
	char server_host[PBS_MAXHOSTNAME+1];
	char hn1[PBS_MAXHOSTNAME+1];

	if (pbs_conf.pbs_leaf_name) {
		char *endp;
		snprintf(server_host, sizeof(server_host), "%s", pbs_conf.pbs_leaf_name);
                endp = strchr(server_host, ','); /* find the first name */
                if (endp)
                        *endp = '\0';
                endp = strchr(server_host, ':'); /* cut out the port */
		if (endp)
			*endp = '\0';
	} else {
		if ((gethostname(server_host, (sizeof(server_host) - 1)) == -1) ||
			(get_fullhostname(server_host, server_host, (sizeof(server_host) - 1)) == -1)) {
			log_err(-1, __func__, "Unable to get my host name");
			return -1;
		}
	}

	/* both secondary and primary should be set or neither set */
	if ((pbs_conf.pbs_secondary == NULL) && (pbs_conf.pbs_primary == NULL))
		return 1;
	if ((pbs_conf.pbs_secondary == NULL) || (pbs_conf.pbs_primary == NULL))
		return -1;

	if (get_fullhostname(pbs_conf.pbs_primary, hn1, (sizeof(hn1) - 1))==-1) {
		log_err(-1, __func__, "Unable to get full host name of primary");
		return -1;
	}

	if (strcmp(hn1, server_host) == 0)
		return 1;	/* we are the listed primary */

	if (get_fullhostname(pbs_conf.pbs_secondary, hn1, (sizeof(hn1) - 1))==-1) {
		log_err(-1, __func__, "Unable to get full host name of secondary");
		return -1;
	}
	if (strcmp(hn1, server_host) == 0)
		return 0;	/* we are the secondary */

	return -1;		/* cannot be neither */
}
/**
 * @brief
 * 		Describes the usage of the command in case of incorrect usage
 *
 * @param[in]	prog	-	program name
 */
#ifdef WIN32
void
usage2(char *prog)
{
	printf("================================================================================\n");
	printf("Usage Info: %s [-R|-U|-N] %s\n", prog, usage);
	printf("\nTo run as a standalone server: %s -N <other options...>\n", prog);
	printf("To register as a service: %s -R\n", prog);
	printf("To unregister the service: %s -U\n", prog);
	printf("To run as a service: %s <other options...>\n", prog);
	printf("To output PBSPro version and exit: %s --version\n", prog);
	printf("================================================================================\n");

}

/**
 * @brief
 * 		log the rpp failure message
 *
 * @param[in]	mess	-	message to be logged.
 */
void
log_rppfail(char *mess)
{
	log_event(PBSEVENT_DEBUG, LOG_DEBUG,
		PBS_EVENTCLASS_SERVER, "rpp", mess);
}

/*
 * @brief
 *		This is the log handler for tpp implemented in the daemon. The pointer to
 *		this function is used by the Libtpp layer when it needs to log something to
 *		the daemon logs
 *
 * @param[in] level   - Logging level
 * @param[in] objname - Name of the object about which logging is being done
 * @param[in] mess   - The log message
 *
 */
static void
log_tppmsg(int level, const char *objname, char *mess)
{
	char id[2*PBS_MAXHOSTNAME];
	int thrd_index;
	int etype = log_level_2_etype(level);

	thrd_index = tpp_get_thrd_index();
	if (thrd_index == -1)
		snprintf(id, sizeof(id), "%s(Main Thread)", (objname != NULL) ? objname : msg_daemonname);
	else
		snprintf(id, sizeof(id), "%s(Thread %d)", (objname != NULL) ? objname : msg_daemonname, thrd_index);

	log_event(etype, PBS_EVENTCLASS_TPP, level, id, mess);
	DBPRT((mess));
	DBPRT(("\n"));
}

DWORD WINAPI
main_thread(pv)
void    *pv;
{
	struct  arg_param *p = (struct arg_param *)pv;
	int      argc;
	char **argv;
#else
/**
 * @brief
 * 		the entry point of the pbs_sched.
 */
int
main(int argc, char *argv[])
{
#endif
	char		zone_dir[MAXPATHLEN];
	int		go, c, errflg = 0, rc;
#ifdef WIN32
	int		try = 3;
#endif
	int		lockfds;
	int		t = 1;

#ifdef WIN32
	int		pid;
	int	pmode = S_IREAD | S_IWRITE;
#else
	pid_t		pid;
	int	pmode = 0600;
#endif

	char		host[PBS_MAXHOSTNAME+1];

#ifndef DEBUG
	char		*dbfile = "sched_out";
#endif

#ifndef WIN32
	struct	sigaction	act;
	sigset_t	oldsigs;
#endif

	extern	char	*optarg;
	extern	int	optind, opterr;
	extern	int	rpp_fd;
	fd_set		fdset;
	int             opt_no_restart = 0;
	char	       *runjobid = NULL;
	int		schedinit(void);
	int		schedule(int com, int connector, char *runjid);
#ifdef _POSIX_MEMLOCK
	int		do_mlockall = 0;
#endif	/* _POSIX_MEMLOCK */
	int		alarm_time = 0;
	char		lockfile[MAXPATHLEN+1];
        time_t		rpp_advise_timeout = 30;	/* rpp_read timeout */

#ifdef WIN32
	void	die(int);
	struct linger 	li;
#endif

#ifndef	WIN32
	/*the real deal or show version and exit?*/
	execution_mode(argc, argv);
#endif

#ifndef DEBUG
#ifdef WIN32
	SERVICE_STATUS ss;

	argc = p->argc;
	argv = p->argv;

	ZeroMemory(&ss, sizeof(ss));
	ss.dwCheckPoint         = 0;
	ss.dwServiceType        = SERVICE_WIN32_OWN_PROCESS;
	ss.dwCurrentState       = g_dwCurrentState;
	ss.dwControlsAccepted   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
	ss.dwWaitHint = 3000;

	if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
	/* load the pbs conf file */
	if (pbs_loadconf(0) == 0) {
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_BAD_CONFIGURATION;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
		return (1);
	}

	if (!isAdminPrivilege(getlogin())) {
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState       = g_dwCurrentState;
		ss.dwWin32ExitCode = CO_E_LAUNCH_PERMSSION_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
		fprintf(stderr, "%s: Must be run by root\n", argv[0]);
		return (1);
	}
#else
	if ((geteuid() != 0) || (getuid() != 0)) {
		fprintf(stderr, "%s: Must be run by root\n", argv[0]);
		return (1);
	}
#endif	/* WIN32 */
#endif	/* DEBUG */

	/* set single threaded mode */
	pbs_client_thread_set_single_threaded_mode();
	/* disable attribute verification */
	set_no_attribute_verification();

	/* initialize the thread context */
	if (pbs_client_thread_init_thread_context() != 0) {
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_OUTOFMEMORY;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif
		fprintf(stderr, "%s: Unable to initialize thread context\n", argv[0]);
		return (1);
	}

	if (!pbs_conf.start_sched) {
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState       = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_BAD_ENVIRONMENT;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif
		return (0);
	}

	/*
	 * set the zoneinfo directory to $PBS_EXEC/zoneinfo.
	 * This is used for standing reservations user of libical
	 */
	sprintf(zone_dir, "%s%s", pbs_conf.pbs_exec_path, ICAL_ZONEINFO_DIR);
	set_ical_zoneinfo(zone_dir);

	glob_argv = argv;

	sched_port = pbs_conf.scheduler_service_port;
	pbs_rm_port = pbs_conf.manager_service_port;

#ifndef WIN32
	segv_start_time = segv_last_time = time(NULL);
#endif

	opterr = 0;
	while ((c = getopt(argc, argv, "lL:S:I:R:d:p:c:a:n")) != EOF) {
		switch (c) {
			case 'l':
#ifdef _POSIX_MEMLOCK
				do_mlockall = 1;
#else
				fprintf(stderr, "-l option - mlockall not supported");
#endif	/* _POSIX_MEMLOCK */
				break;
			case 'L':
				logfile = optarg;
				break;
			case 'I':
				sc_name = optarg;
				break;
			case 'S':
				sched_port = atoi(optarg);
				if (sched_port == 0) {
					fprintf(stderr,
						"%s: illegal port\n", optarg);
					errflg = 1;
				}
				break;
			case 'R':
				if ((pbs_rm_port = atoi(optarg)) == 0) {
					(void)fprintf(stderr, "%s: bad -R %s\n",
						argv[0], optarg);
					return 1;
				}
				break;
			case 'd':
				if (pbs_conf.pbs_home_path != NULL)
					free(pbs_conf.pbs_home_path);
				pbs_conf.pbs_home_path = optarg;
				break;
			case 'p':
				dbfile = optarg;
				break;
			case 'c':
				configfile = optarg;
				break;
			case 'a':
				alarm_time = atoi(optarg);
				if (alarm_time == 0) {
					fprintf(stderr,
						"%s: bad alarm time\n", optarg);
					errflg = 1;
				}
				fprintf(stderr, "The -a option is deprecated.  Please see the \'%s\' scheduler attribute\n", ATTR_sched_cycle_len);
				break;
			case 'n':
				opt_no_restart = 1;
				break;
			default:
				errflg = 1;
				break;
		}
	}

	if (sc_name == NULL) {
		sc_name = PBS_DFLT_SCHED_NAME;
		dflt_sched = 1;
	}

	if (errflg) {
#ifdef WIN32
		usage2(argv[0]);
#else
		fprintf(stderr, "usage: %s %s\n", argv[0], usage);
		fprintf(stderr, "       %s --version\n", argv[0]);
#endif

		exit(1);
	}

	/* Save the original working directory for "restart" */
	if ((oldpath = getcwd(NULL, MAXPATHLEN)) == NULL) {
		fprintf(stderr, "cannot get current working directory\n");
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_DIRECTORY;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif

		exit(1);
	}
	if (dflt_sched) {
		(void)sprintf(log_buffer, "%s/sched_priv", pbs_conf.pbs_home_path);
	} else {
		(void)sprintf(log_buffer, "%s/sched_priv_%s", pbs_conf.pbs_home_path, sc_name);
	}

#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
#ifdef WIN32
	/* For windows, do not check full path. Allow system to put in */
	/* default permissions for top-level directories */
	c  = chk_file_sec(log_buffer, 1, 0, WRITES_MASK, 0);
	c |= chk_file_sec(pbs_conf.pbs_environment, 0, 0, WRITES_MASK, 0);
#else
	c  = chk_file_sec(log_buffer, 1, 0, S_IWGRP|S_IWOTH, 1);
	c |= chk_file_sec(pbs_conf.pbs_environment, 0, 0, S_IWGRP|S_IWOTH, 0);
#endif
	if (c != 0) exit(1);
#endif  /* not DEBUG and not NO_SECURITY_CHECK */
	if (chdir(log_buffer) == -1) {
		perror("chdir");
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_DIRECTORY;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif
		exit(1);
	}
	if (dflt_sched) {
		(void)sprintf(path_log,   "%s/sched_logs", pbs_conf.pbs_home_path);
	} else {
		(void)sprintf(path_log,   "%s/sched_logs_%s", pbs_conf.pbs_home_path, sc_name);
	}

	/* The following is code to reduce security risks                */
	/* start out with standard umask, system resource limit infinite */

#ifndef WIN32
	umask(022);
#endif
	if (setup_env(pbs_conf.pbs_environment)==-1) {

#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_BAD_CONFIGURATION;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif
		exit(1);
	}


#ifndef WIN32
	c = getgid();
	(void)setgroups(1, (gid_t *)&c);	/* secure suppl. groups */
#endif

#ifndef DEBUG
#ifndef WIN32
	{
		struct rlimit rlimit;

		rlimit.rlim_cur = RLIM_INFINITY;
		rlimit.rlim_max = RLIM_INFINITY;
		(void)setrlimit(RLIMIT_CPU,   &rlimit);
		(void)setrlimit(RLIMIT_FSIZE, &rlimit);
		(void)setrlimit(RLIMIT_DATA,  &rlimit);
		(void)setrlimit(RLIMIT_STACK, &rlimit);
#ifdef  RLIMIT_RSS
		(void)setrlimit(RLIMIT_RSS  , &rlimit);
#endif  /* RLIMIT_RSS */
#ifdef  RLIMIT_VMEM
		(void)setrlimit(RLIMIT_VMEM  , &rlimit);
#endif  /* RLIMIT_VMEM */
	}
#endif	/* not WIN32 */
#endif	/* DEBUG */


#ifdef WIN32
	/*
	 * let SCM wait 10 seconds for log_open() to complete
	 * as it does network interface query which can take time
	 */

	ss.dwCheckPoint++;
	ss.dwWaitHint = 60000;
	if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif

	if (log_open(logfile, path_log) == -1) {
		fprintf(stderr, "%s: logfile could not be opened\n", argv[0]);
		exit(1);
	}


#ifdef WIN32
	/* let SCM wait 20 seconds for secure_misc_files() to complete */
	ss.dwCheckPoint++;
	ss.dwWaitHint = 20000;
	if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);

	secure_misc_files();

	/* let SCM wait 30 seconds for secure_sched_files() to complete */
	ss.dwCheckPoint++;
	ss.dwWaitHint = 30000;
	if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);

	secure_sched_files();
#endif

	/*we log here because the log wasn't open when the options were parsed*/
	if (alarm_time)
		log_record(PBSEVENT_SCHED, PBS_EVENTCLASS_SCHED, LOG_NOTICE, "", "The -a option was given on the command line.  This is deprecated.  Please seee the \'sched_cycle_length\' scheduler attribute");
#ifdef WIN32
	winsock_init();

/* Let's do an extra validity check */

	if (check_executor() == 1) { /* failed on check for root */
		log_err(-1, msg_daemonname, winlog_buffer);
		return (3);
	}
	if (strlen(winlog_buffer) > 0) {

		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
			LOG_WARNING, msg_daemonname, winlog_buffer);
	}
#endif
	if (gethostname(host, (sizeof(host) - 1)) == -1) {
#ifdef WIN32
		errno = WSAGetLastError();
#endif
		log_err(errno, __func__, "gethostname");
		die(0);
	}

#ifdef WIN32
	/*
	 * Something gets messed up in socket initialization when we called setgroups().
	 * So we have to retry getting a socket...
	 */
	try = 3;
	while (try >= 0) {
		try--;
		if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			errno = WSAGetLastError();
			continue;
		} else
			break;
	}

	if (server_sock < 0) {
		log_err(errno, __func__, "socket");
		die(0);
	}

	li.l_onoff = 1;
	li.l_linger = 5;
	setsockopt(server_sock, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li));
#else
	if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		log_err(errno, __func__, "socket");
		die(0);
	}
#endif

	if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR,
		(char *)&t, sizeof(t)) == -1) {
#ifdef WIN32
		errno = WSAGetLastError();
#endif
		log_err(errno, __func__, "setsockopt");
		die(0);
	}


	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(sched_port);
	saddr.sin_addr.s_addr = INADDR_ANY;
	if (bind(server_sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
#ifdef WIN32
		errno = WSAGetLastError();
#endif
		log_err(errno, __func__, "bind");
		die(0);
	}


	if (listen(server_sock, 5) < 0) {
#ifdef WIN32
		errno = WSAGetLastError();
#endif
		log_err(errno, __func__, "listen");
		die(0);
	}

	okclients = (pbs_net_t *)calloc(START_CLIENTS, sizeof(pbs_net_t));
	if (okclients == NULL) {
		log_err(errno, __func__, "Unable to allocate memory (malloc error)");
		die(0);
	}
	addclient(LOCALHOST_SHORTNAME);   /* who has permission to call MOM */
	addclient(host);
	if (pbs_conf.pbs_primary && pbs_conf.pbs_secondary) {
		/* Failover is configured when both primary and secondary are set. */
		addclient(pbs_conf.pbs_primary);
		addclient(pbs_conf.pbs_secondary);
	} else if (pbs_conf.pbs_server_host_name) {
		/* Failover is not configured, but PBS_SERVER_HOST_NAME is. */
		addclient(pbs_conf.pbs_server_host_name);
	}

	if (configfile) {
		if (read_config(configfile) != 0)
			die(0);
	}

	if ((c = are_we_primary()) == 1) {
		lockfds = open("sched.lock", O_CREAT|O_WRONLY, pmode);
	} else if (c == 0) {
		lockfds = open("sched.lock.secondary", O_CREAT|O_WRONLY, pmode);
	} else {
		log_err(-1, "pbs_sched", "neither primary or secondary server");
		exit(1);
	}

	if (lockfds < 0) {
		log_err(errno, __func__, "open lock file");
		exit(1);
	}

	lock_out(lockfds, F_WRLCK);

#ifdef WIN32
	(void)sprintf(lockfile, "%s/%s/sched.lock", pbs_conf.pbs_home_path,
		PBS_SCHED_PRIVATE);
	secure_file(lockfile, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif

	fullresp(0);

#ifdef WIN32
	signal(SIGABRT, die);
	signal(SIGILL, die);
	signal(SIGINT, die);
	signal(SIGSEGV, die);
	signal(SIGTERM, die);

	/*
	 * Specifically don't catch SIGBREAK for when one logs out in
	 * windows, SIGBREAK is sent to the service
	 */
#else
	if (sigemptyset(&allsigs) == -1) {
		perror("sigemptyset");
		exit(1);
	}
	if (sigprocmask(SIG_SETMASK, &allsigs, NULL) == -1) {	/* unblock */
		perror("sigprocmask");
		exit(1);
	}
	act.sa_flags = 0;
	sigaddset(&allsigs, SIGHUP);    /* remember to block these */
	sigaddset(&allsigs, SIGINT);    /* during critical sections */
	sigaddset(&allsigs, SIGTERM);   /* so we don't get confused */
	act.sa_mask = allsigs;

	act.sa_handler = restart;       /* do a restart on SIGHUP */
	sigaction(SIGHUP, &act, NULL);

	act.sa_handler = die;           /* bite the biscuit for all following */
	sigaction(SIGINT, &act, NULL);
	sigaction(SIGTERM, &act, NULL);

	if (!opt_no_restart) {
		act.sa_handler = on_segv;
		sigaction(SIGSEGV, &act, NULL);
		sigaction(SIGBUS, &act, NULL);
	}
#endif

	/*
	 *  Local initialization stuff
	 */
	if (schedinit()) {
		(void)sprintf(log_buffer,
			"local initialization failed, terminating");
		log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO,
			__func__, log_buffer);
		exit(1);
	}

#ifndef	DEBUG
#ifndef WIN32
	lock_out(lockfds, F_UNLCK);
	if ((pid = fork()) == -1) {     /* error on fork */
		perror("fork");
		exit(1);
	} else if (pid > 0)               /* parent exits */
		exit(0);
	if ((pid =setsid()) == -1) {
		perror("setsid");
		exit(1);
	}
	lock_out(lockfds, F_WRLCK);
#endif	/* WIN32 */
	freopen(dbfile, "a", stdout);

#ifdef WIN32
	secure_file(dbfile, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif

	setvbuf(stdout, NULL, _IONBF, 0);
	dup2(fileno(stdout), fileno(stderr));
#else
	lock_out(lockfds, F_WRLCK);
	setvbuf(stdout, NULL, _IOLBF, 0);
	setvbuf(stderr, NULL, _IOLBF, 0);
#endif
	pid = getpid();
	daemon_protect(0, PBS_DAEMON_PROTECT_ON);

#ifdef WIN32
	setvbuf(stdout, NULL, _IONBF, 0);
	setvbuf(stderr, NULL, _IONBF, 0);
	fclose(stdin);
#else
	freopen("/dev/null", "r", stdin);
#endif

/* write schedulers pid into lockfile */
#ifdef WIN32
	lseek(lockfds, (off_t)0, SEEK_SET);
#else
	(void)ftruncate(lockfds, (off_t)0);
#endif
	(void)sprintf(log_buffer, "%d\n", pid);


	(void)write(lockfds, log_buffer, strlen(log_buffer));

#ifdef _POSIX_MEMLOCK
	if (do_mlockall == 1) {
		if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
			log_err(errno, __func__, "mlockall failed");
		}
	}
#endif	/* _POSIX_MEMLOCK */

	(void)sprintf(log_buffer, msg_startup1, PBS_VERSION, 0);
	log_event(PBSEVENT_SYSTEM | PBSEVENT_ADMIN | PBSEVENT_FORCE,
		LOG_NOTICE, PBS_EVENTCLASS_SERVER, msg_daemonname, log_buffer);

	sprintf(log_buffer, "%s startup pid %d", argv[0], pid);
	log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__, log_buffer);

	rpp_fd = -1;
	if (pbs_conf.pbs_use_tcp == 1) {
		fd_set selset;
		struct timeval tv;
		char *nodename = NULL;
		sprintf(log_buffer, "Out of memory");
		if (pbs_conf.pbs_leaf_name) {
			char *p=NULL;
			nodename = strdup(pbs_conf.pbs_leaf_name);
			/* reset pbs_leaf_name to only the first leaf name with port */
			p = strchr(pbs_conf.pbs_leaf_name, ','); /* keep only the first leaf name */
			if ( p != NULL)
				*p = '\0';
			p = strchr(pbs_conf.pbs_leaf_name, ':'); /* cut out the port */
			if ( p != NULL)
				*p = '\0';
		} else {
			nodename = get_all_ips(host, log_buffer, sizeof(log_buffer) - 1);
		}
		if (!nodename) {
			log_err(-1, "pbsd_main", log_buffer);
			fprintf(stderr, "%s\n", "Unable to determine TPP node name");
			return (1);
		}

		/* set tpp function pointers */
		set_tpp_funcs(log_tppmsg);

		if (pbs_conf.auth_method == AUTH_RESV_PORT) {
		rc = set_tpp_config(&pbs_conf, &tpp_conf, nodename, sched_port,
				pbs_conf.pbs_leaf_routers, pbs_conf.pbs_use_compression, TPP_AUTH_RESV_PORT, NULL, NULL);
		} else {
			/* for all non-resv-port based authentication use a callback from TPP */
			rc = set_tpp_config(&pbs_conf, &tpp_conf, nodename, sched_port,
					pbs_conf.pbs_leaf_routers, pbs_conf.pbs_use_compression,
					TPP_AUTH_EXTERNAL, get_ext_auth_data, validate_ext_auth_data);
		}

		free(nodename);

		if (rc == -1) {
			fprintf(stderr, "Error setting TPP config\n");
			return -1;
		}

		if ((rpp_fd = tpp_init(&tpp_conf)) == -1) {
			fprintf(stderr, "rpp_init failed\n");
			return -1;
		}
		/*
		 * Wait for net to get restored, ie, app to connect to routers
		 */
		FD_ZERO(&selset);
		FD_SET(rpp_fd, &selset);
		tv.tv_sec = 5;
		tv.tv_usec = 0;
		select(FD_SETSIZE, &selset, NULL, NULL, &tv);

		rpp_poll(); /* to clear off the read notification */
	} else {
		/* set rpp function pointers */
		set_rpp_funcs(log_rppfail);

		/* set a timeout for rpp_read operations */
		if (rpp_advise(RPP_ADVISE_TIMEOUT, &rpp_advise_timeout) != 0) {
			log_err(errno, __func__, "rpp_advise");
			die(0);
		}
	}

#ifdef WIN32
	ss.dwCheckPoint = 0;
	g_dwCurrentState = SERVICE_RUNNING;
	ss.dwCurrentState = g_dwCurrentState;
	if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif
	FD_ZERO(&fdset);
	for (go=1; go;) {
		int	cmd;

#ifdef WIN32
		if (!stalone && (g_dwCurrentState != SERVICE_RUNNING))
			break;
#endif
		/*
		 * with TPP, we don't need to drive rpp_io(), so
		 * no need to add it to be monitored
		 */
		if (pbs_conf.pbs_use_tcp == 0 && rpp_fd != -1)
			FD_SET(rpp_fd, &fdset);

		FD_SET(server_sock, &fdset);
		if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) == -1) {
			if (errno != EINTR) {
				log_err(errno, __func__, "select");
				die(0);
			}
			continue;
		}

		if (pbs_conf.pbs_use_tcp == 0 && rpp_fd != -1 && FD_ISSET(rpp_fd, &fdset)) {
			if (rpp_io() == -1)
				log_err(errno, __func__, "rpp_io");
		}
		if (!FD_ISSET(server_sock, &fdset))
			continue;

		cmd = server_command(&runjobid);

		/*based on cmd: send|not scheduler's PBS version to server*/
		update_svr_schedobj(connector, cmd, alarm_time);

#ifndef WIN32
		if (sigprocmask(SIG_BLOCK, &allsigs, &oldsigs) == -1)
			log_err(errno, __func__, "sigprocmaskSIG_BLOCK)");

		if (!opt_no_restart)
			segv_last_time = time(NULL);
#endif

		if (schedule(cmd, connector, runjobid))	/* magic happens here */
			go = 0;

		if (second_connection != -1) {
#ifdef WIN32
			(void)closesocket(second_connection);
#else
			close(second_connection);
#endif
			second_connection = -1;
		}

		server_disconnect(connector);

		if (runjobid != NULL) {
			free(runjobid);
			runjobid = NULL;
		}

#ifndef WIN32
		if (sigprocmask(SIG_SETMASK, &oldsigs, NULL) == -1)
			log_err(errno, __func__, "sigprocmask(SIG_SETMASK)");
#endif
}

	sprintf(log_buffer, "%s normal finish pid %d", argv[0], pid);
	log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__, log_buffer);

	lock_out(lockfds, F_UNLCK);	/* unlock  */
	(void)close(lockfds);
	if (are_we_primary() == 1)
		(void)unlink("sched.lock");
	else if (c == 0)
		(void)unlink("sched.lock.secondary");

	if (server_sock >= 0) {
#ifdef WIN32
		(void)closesocket(server_sock);
#else
		(void)close(server_sock);
#endif
	}

#ifdef WIN32
	destroy_env_avltree();

	/* The following is needed since pbsSchedHandler() can prematurely */
	/* stop the main_thread() but still expect normal termination */
	g_dwCurrentState = SERVICE_STOPPED;
	ss.dwCurrentState  = g_dwCurrentState;
	ss.dwWin32ExitCode = 0;
	if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif

	exit(0);
}

#ifdef WIN32
/**
 * @brief
 * 		the entry point of the pbs_sched.
 */
int
main(int argc, char *argv[])
{
	int reg = 0;
	int unreg = 0;
	SC_HANDLE schManager;
	SC_HANDLE schSelf;
	TCHAR	  szFileName[MAX_PATH];

	/*the real deal or show version and exit?*/

	execution_mode(argc, argv);
	if(set_msgdaemonname("pbs_sched")) {
		fprintf(stderr, "Out of memory\n");
		return 1;
	}

	if (argc > 1) {
		if (strcmp(argv[1], "-R") == 0)
			reg = 1;
		else if (strcmp(argv[1], "-U") == 0)
			unreg = 1;
		else if (strcmp(argv[1], "-N") == 0)
			stalone = 1;
	}

	if (reg || unreg) {
		schManager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
		if (schManager == 0) {
			ErrorMessage("OpenSCManager");
		}

		if (reg) {
			GetModuleFileName(0, szFileName, sizeof(szFileName)/sizeof(*szFileName));
			printf("Installing service %s\n", g_PbsSchedName);
			schSelf =
				CreateService(schManager, g_PbsSchedName, __TEXT("PBS SCHED"),
				SERVICE_ALL_ACCESS,
				SERVICE_WIN32_OWN_PROCESS,
				SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
				replace_space(szFileName, ""), 0, 0, 0, 0, 0);

			if (schSelf) {
				SERVICE_FAILURE_ACTIONS sfa;
				SC_ACTION               sca[1];

				printf("Service %s installed successfully!\n", g_PbsSchedName);

				sca[0].Type = SC_ACTION_RESTART;
				sca[0].Delay = 60*1000;
				sfa.dwResetPeriod = INFINITE;
				sfa.lpRebootMsg = NULL;
				sfa.lpCommand = NULL;
				sfa.cActions = 1;
				sfa.lpsaActions = sca;
				ChangeServiceConfig2(schSelf, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa);
			} else {
				ErrorMessage("CreateService");
			}

			if (schSelf != 0)
				CloseServiceHandle(schSelf);
		} else if (unreg) {
			schSelf = OpenService(schManager, g_PbsSchedName, DELETE);

			if (schSelf) {
				if (DeleteService(schSelf)) {
					printf("Service %s uninstalled successfully!\n", g_PbsSchedName);
				} else {
					ErrorMessage("DeleteService");
				}
			} else {
				ErrorMessage("OpenService failed");
			}
			if (schSelf != 0)
				CloseServiceHandle(schSelf);
		}

		if (schManager != 0)
			CloseServiceHandle(schManager);
	} else if (stalone) {
		struct arg_param *pap;
		int	i, j;

		pap = create_arg_param();
		if (pap == NULL)
			ErrorMessage("create_arg_param");

		pap->argc = argc-1;	/* don't pass the second argument */
		for (i=j=0; i < argc; i++) {
			if (i == 1)
				continue;
			if ((pap->argv[j] = strdup(argv[i])) == NULL) {
				free_arg_param(pap);
				ErrorMessage("strdup");
			}
			j++;
		}
		main_thread((void *)pap);

		free_arg_param(pap);
	} else {	/* running as a service */
		SERVICE_TABLE_ENTRY rgste[] = { {(TCHAR*)g_PbsSchedName, PbsSchedMain },
			{ 0, 0 } };

		if (getenv("PBS_CONF_FILE") == NULL) {
			char conf_path[80];
			char conf_env[80];
			char *p;
			char psave;
			struct stat sbuf;

			if (p = strstr(argv[0], "exec")) {
				psave = *p;
				*p = '\0';
				_snprintf(conf_path, 79, "%spbs.conf", argv[0]);
				*p = psave;
				if (stat(conf_path, &sbuf) == 0) {
					setenv("PBS_CONF_FILE", conf_path, 1);
				}
			}
		}

		if (!StartServiceCtrlDispatcher(rgste)) {
			ErrorMessage("StartServiceCntrlDispatcher");
		}
	}
	return (0);
}
/**
 * @brief
 * 		Pbs scheduler main function
 *
 * @param[in]	dwArgc	-	argument count.
 * @param[in]	rgszArgv	-	argument array
 */
void WINAPI
PbsSchedMain(DWORD dwArgc, LPTSTR *rgszArgv)
{
	DWORD	dwTID;
	DWORD	dwWait;
	SERVICE_STATUS	ss;
	DWORD	i;

	struct arg_param *pap;

	g_ssHandle = RegisterServiceCtrlHandler(g_PbsSchedName, PbsSchedHandler);
	if (g_ssHandle == 0) {
		ErrorMessage("RegisterServiceCtrlHandler");
	}

	pap = create_arg_param();
	if (pap == NULL)
		return;
	pap->argc = dwArgc;

	for (i=0; i < dwArgc; i++) {
		if ((pap->argv[i] = strdup(rgszArgv[i])) == NULL) {
			free_arg_param(pap);
			ErrorMessage("strdup");
		}
	}

	g_hthreadMain = (HANDLE) _beginthreadex(0, 0,  main_thread, pap, 0, &dwTID);
	if (g_hthreadMain == 0) {
		(void)free_arg_param(pap);
		ErrorMessage("CreateThread");
	}

	dwWait = WaitForSingleObject(g_hthreadMain, INFINITE);
	if (dwWait != WAIT_OBJECT_0) {
		(void)free_arg_param(pap);
		ErrorMessage("WaitForSingleObject");
	}

	// NOTE: Update the global service state variable to indicate
	//      that the server has STOPPED. Use this to ACK the SCM
	//      that the service has stopped using SetServiceStatus.
	ZeroMemory(&ss, sizeof(ss));
	ss.dwServiceType        = SERVICE_WIN32_OWN_PROCESS;
	ss.dwCurrentState       = SERVICE_STOPPED;
	ss.dwControlsAccepted   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

	if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);

	free_arg_param(pap);
}
/**
 * @brief
 * 		Handler function for the Pbs Scheduler.
 *
 * @param[in]	dwControl	-	Service control request.
 */
void WINAPI
PbsSchedHandler(DWORD dwControl)
{
	SERVICE_STATUS ss;

	ZeroMemory(&ss, sizeof(ss));
	ss.dwServiceType        = SERVICE_WIN32_OWN_PROCESS;
	ss.dwCurrentState       = g_dwCurrentState;
	ss.dwControlsAccepted   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

	switch (dwControl) {
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			// DONE: When you receive a stop request, update the global state
			//      variable to indicate that a STOP is pending. You need
			//      to then ACK the SCM by calling SetServiceStatus. Set
			//      the check point to 1 and the wait hint to 1 second,
			//      since we are going to wait for the server to shutdown.

			g_dwCurrentState    = SERVICE_STOP_PENDING;
			ss.dwCurrentState   = g_dwCurrentState;
			ss.dwCheckPoint     = 1;
			ss.dwWaitHint       = 1000;
			if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);

			log_close(1);
			CloseHandle(g_hthreadMain);

			if (server_sock >= 0) {
				closesocket(server_sock); /* causes main thread to quit */
				server_sock = INVALID_SOCKET;
			}

			break;

		default:
			if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
			break;
	}
}
#endif
