/*
 * 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_tmrsh.c
 * @brief
 * pbs_tmrsh - a replacement for rsh using the Task Management API
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "cmds.h"
#include "tm.h"
#include "pbs_version.h"

char		*host = NULL;

extern char *get_ecname(int rc);

/**
 * @brief
 * 	displays how to use pbs_tmrsh
 *
 * @param[in] id - command name i.e pbs_tmrsh
 *
 * @return Void
 *
 */
void
usage(char *id)
{
	fprintf(stderr, "usage: %s [-n][-l username] host [-n][-l username] command\n", id);
	fprintf(stderr, "       %s --version\n", id);
	exit(255);
}

/**
 * @brief
 *  	returns the username
 *
 * @return string
 * @retval "username" 
 *
 */
char *
myname(void)
{
	uid_t		me = getuid();
	struct passwd	*pent;

	if ((pent = getpwuid(me)) == NULL)
		return "";
	else
		return pent->pw_name;
}

#ifndef INADDR_NONE
#define INADDR_NONE	(in_addr_t)0xFFFFFFFF
#endif

/**
 * @brief
 *	Check the host to a line read from PBS_NODEFILE.
 *	The PBS_NODEFILE will contain node names.  We want to be able
 *	to accept IP addresses for the host.
 *
 * @param[in] line - line from PBS_NODEFILE
 * 
 * @return  	Error code
 * @retval	1 - Success i.e matched	
 * @retval	0 - Failure i.e not matched
 *
 */
int
host_match(char *line)
{
	int	len = strlen(line);
	static	char	domain[PBS_MAXHOSTNAME+1];
	char	fullhost[PBS_MAXHOSTNAME+1];
	static	struct in_addr	addr;
	static	int		addrvalid = -1;

	if (line[len-1] == '\n')
		line[len-1] = '\0';

	if (strcmp(line, host) == 0)
		return 1;

	if (addrvalid == -1) {
		addr.s_addr = inet_addr(host);
		addrvalid = (addr.s_addr == INADDR_NONE) ? 0 : 1;
	}

	if (addrvalid) {	/* compare IP addresses */
		struct	hostent	*hp = gethostbyname(line);
		int	i;

		if (hp == NULL)
			return 0;

		for (i=0; hp->h_addr_list[i]; i++) {
			if (memcmp(&addr, hp->h_addr_list[i],
				hp->h_length) == 0)
				return 1;
		}
		return 0;
	}

	if (domain[0] == '\0') {
		if (getdomainname(domain, (sizeof(domain) - 1)) == -1) {
			perror("getdomainname");
			exit(255);
		}
		if (domain[0] == '\0') {
			int	i;
			char	*dot;

			if (gethostname(domain, (sizeof(domain) - 1)) == -1) {
				perror("getdomainname");
				exit(255);
			}
			if (domain[0] == '\0')
				return 0;
			if ((dot = strchr(domain, '.')) == NULL)
				return 0;
			for (i=0, dot++; *dot; i++, dot++)
				domain[i] = *dot;
			domain[i] = '\0';
		}
	}
	strcpy(fullhost, line);
	strcat(fullhost, ".");
	strcat(fullhost, domain);

	if (strcmp(fullhost, host) == 0)
		return 1;

	return 0;
}

int
main(int argc, char *argv[], char *envp[])
{
	char		*id;
	char		*jobid;
	int		i, arg;
	FILE		*fp;
	int		numnodes;
	int		err = 0;
	int		rc, exitval;
	struct tm_roots	rootrot;
	char		*nodefile;
	tm_node_id	*nodelist;
	tm_event_t	event;
	tm_task_id	tid;
	char		line[256], *cp;

	/*test for real deal or just version and exit*/

	execution_mode(argc, argv);

#ifdef WIN32

	winsock_init();
#endif

	id = argv[0];
	if (argc < 3)
		usage(id);

	for (arg=1; arg<argc; arg++) {
		char	*c = argv[arg];
		char	lopt[] = "-l";
		int	len = sizeof(lopt)-1;

		if (*c == '-') {	/* option */
			if (strcmp(c, "-n") == 0)	/* noop */
				continue;

			if (strncmp(c, lopt, len) == 0) {	/* login name */
				if (strlen(c) == len) {
					arg++;
					if (arg == argc) {
						err = 1; /* no args left */
						break;
					}
					c = argv[arg];
				} else			/* -lname */
					c += len;

				if (strcmp(c, myname()) != 0) {
					fprintf(stderr, "%s: bad user \"%s\"\n",
						id, c);
					err = 1;
				}
			}
			else {		/* unknown option */
				err = 1;
				break;
			}
		} else if (host == NULL)
			host = c;	/* first non-option is host */
		else
			break;		/* host is set, must be command */
	}

	/*
	 **	If there was an error processing arguments or there is
	 **	no command, exit.
	 */
	if (err || (argc == arg) || (host == NULL))
		usage(id);

	if (getenv("PBS_ENVIRONMENT") == 0) {
		fprintf(stderr, "%s: not executing under PBS\n", id);
		return 255;
	}
	if ((jobid = getenv("PBS_JOBID")) == NULL) {
		fprintf(stderr, "%s: PBS jobid not in environment\n", id);
		return 255;
	}

	/*
	 **	Set up interface to the Task Manager
	 */
	if ((rc = tm_init(NULL, &rootrot)) != TM_SUCCESS) {
		fprintf(stderr, "%s: tm_init: %s\n", id, get_ecname(rc));
		return 255;
	}

	if ((rc = tm_nodeinfo(&nodelist, &numnodes)) != TM_SUCCESS) {
		fprintf(stderr, "%s: tm_nodeinfo: %s\n", id, get_ecname(rc));
		return 255;
	}

	/*
	 ** Check which node number the host is.
	 */
	if ((nodefile = getenv("PBS_NODEFILE")) == NULL) {
		fprintf(stderr, "%s: cannot find PBS_NODEFILE\n", id);
		return 255;
	}
	if ((fp = fopen(nodefile, "r")) == NULL) {
		perror(nodefile);
		return 255;
	}

	for (i=0; (cp = fgets(line, sizeof(line), fp)) != NULL; i++) {
		if (host_match(line))
			break;
	}
	fclose(fp);
	if (cp == NULL) {
		fprintf(stderr, "%s: host \"%s\" is not a node in job <%s>\n",
			id, host, jobid);
		return 255;
	}
	if (i >= numnodes) {
		fprintf(stderr, "%s: PBS_NODEFILE contains %d entries, "
			"only %d nodes in job\n", id, i, numnodes);
		return 255;
	}

	if ((rc = tm_spawn(argc-arg, argv+arg, NULL,
		nodelist[i], &tid, &event)) != TM_SUCCESS) {
		fprintf(stderr, "%s: tm_spawn: host \"%s\" err %s\n",
			id, host, get_ecname(rc));
	}

	rc = tm_poll(TM_NULL_EVENT, &event, 1, &err);
	if (rc != TM_SUCCESS || event == TM_ERROR_EVENT) {
		fprintf(stderr, "%s: tm_poll(spawn): host \"%s\" err %s %d\n",
			id, host, get_ecname(rc), err);
		return 255;
	}

	if ((rc = tm_obit(tid, &exitval, &event)) != TM_SUCCESS) {
		fprintf(stderr, "%s: obit: host \"%s\" err %s\n",
			id, host, get_ecname(rc));
		return 255;
	}

	rc = tm_poll(TM_NULL_EVENT, &event, 1, &err);
	if (rc != TM_SUCCESS || event == TM_ERROR_EVENT) {
		fprintf(stderr, "%s: tm_poll(obit): host \"%s\" err %s %d\n",
			id, host, get_ecname(rc), err);
		return 255;
	}

	tm_finalize();

	return exitval;
}
