/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *	Ohio Trollius
 *	Copyright 1995 The Ohio State University
 *	GDB
 *
 *	$Log: lamshrink.c,v $
 *	Revision 6.4  1999/06/12 17:11:18  kmeyer1
 *	added copyright
 *	
 *	Revision 6.3  1999/06/09 16:36:29  jsquyres
 *	Added <sys/types.h> and <unistd.h> (if they weren't there already) for
 *	getuid() and geteuid().
 *	
 *	Revision 6.2  1999/06/07 01:50:58  jsquyres
 *	Added code to bail if root invokes this program.
 *	Added some error messages, too.  Still may need to add more show_help()
 *	references.
 *	
 *	Revision 6.1  1996/11/23 18:27:50  nevin
 *	Ohio Release
 *	
 * Revision 6.0  96/03/02  01:44:01  gdburns
 * Ohio Release
 * 
 *	Function:	- removes a node from an existing LAM session
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#include <args.h>
#include <debug.h>
#include <dl_inet.h>
#include <events.h>
#include <ksignal.h>
#include <lamnet.h>
#include <portable.h>
#include <preq.h>
#include <priority.h>
#include <rreq.h>
#include <terror.h>

/*
 * local variables
 */
static int		fl_verbose;	/* verbose mode */
static int		fl_debug;	/* debug / help mode */
static char		*hname;		/* host name */
static char		*uname;		/* userid */

static char		*usage =
	"lamshrink [-hv] [-u <userid>] [-w <secs>] <nodeid> <hostname>\n";

/* 
 * local functions
 */
static void		help();

/* 
 * external functions
 */
extern int		inetexec();
extern int		_lam_few();
extern void		nodespin_end();
extern void		nodespin_init();
extern void		nodespin_next();

/*
 * main
 */
int
main(argc, argv)  

int			argc;
char			*argv[];

{
	int		cmdn;		/* # cmd line args */
	int		delay;		/* delay in seconds */
	int		flags;		/* node flags */
	int		index;		/* node index */
	int		r;		/* return code */
	int4		i;
	int4		nodetarget;	/* node to be removed */
	int4		nnodes;		/* # neighbour nodes */
	int4		*pnodes;	/* neighbour nodes */
	char		**cmdv;		/* cmd line args */
	struct direq	*pdiq;		/* dli request */
	struct direply	*pdir;		/* dli reply */
	struct nmsg	nh;		/* message to/from dli */

	/* Ensure that we are not root */

	if (getuid() == 0 || geteuid() == 0) {
	  show_help(NULL, "deny-root", NULL);
	  exit(EACCES);
	}

/*
 * Parse the command line.
 */
	validopts("hv");
	followed("uw");

	if (do_args(&argc, argv)) {
		fprintf(stderr, usage);
		exit(errno);
	}

	if (opt_taken('h')) {
		help();
		exit(0);
	}
/*
 * Initialize LAM.
 */
	if (kinit(PRCMD)) {
		terror("lamshrink (kinit)");
		exit(errno);
	}

	if (nid_parse(&argc, argv) || ((errno = (argc == 2) ? 0 : EUSAGE))) {
		fprintf(stderr, usage);
		kexit(errno);
	}

	hname = argv[1];
	uname = opt_taken('u') ? getparam('u') : 0;
/*
 * Set the flags.
 */
	fl_debug = opt_taken('d');
	fl_verbose = opt_taken('v');
/*
 * Determine the target node.  Allow only one node.
 */
	nid_get(&index, &nodetarget, &flags);

	if (index < 0) {
		fprintf(stderr, usage);
		kexit(EUSAGE);
	}

	nid_get(&index, &nodetarget, &flags);

	if (index != 0) {
		fprintf(stderr, usage);
		kexit(EUSAGE);
	}
/*
 * Obtain all IDs.
 */
	nnodes = getnall();
	if (nnodes < 0) lamfail("lamshrink (getnall)");

	pnodes = (int4 *) malloc((unsigned) nnodes * sizeof(int4));
	if (pnodes == 0) lamfail("lamshrink (malloc)");

	if (getall(pnodes, nnodes)) lamfail("lamshrink (getall)");
/*
 * Send a warning signal to all application processes on the
 * target node.
 */
	if (opt_taken('w')) {
		intparam('w', &delay);
		VERBOSE("signal doomed node ...\n");

		if (rpdoom(nodetarget, SELECT_APPL, 0, SIGFUSE)) {
			lamfail("lamshrink (rpdoom)");
		}

		VERBOSE("pause ...\n");
		sleep(delay);
	}
/*
 * Invoke a node remove procedure on all nodes other than the node
 * being removed.
 */
	pdiq = (struct direq *) nh.nh_data;
	pdir = (struct direply *) nh.nh_data;
	nh.nh_flags = NOBUF;
	nh.nh_msg = 0;

	if (fl_verbose) {
		nodespin_init("update");
	}

	for (i = 0; i < nnodes; ++i) {

		if (pnodes[i] == nodetarget) continue;

		if (fl_verbose) {
			nodespin_next(i);
		}

		pdiq->diq_req = DIQREMLINK;
		pdiq->diq_src_node = (getnodeid() == nodetarget) ? NOTNODEID :
				getnodeid();
		pdiq->diq_src_event = -getpid();
		pdiq->diq_link = nodetarget;
		nh.nh_node = pnodes[i];
		nh.nh_event = EVDLI;
		nh.nh_type = 0;
		nh.nh_length = 0;

		if (getroute(&nh)) lamfail("lamshrink (getroute)");
/*
 * Re-route the local node.
 */
		if (nh.nh_dl_event == nh.nh_event) {
			nh.nh_dl_event = EVDL0;
			nh.nh_dl_link = getnodeid();
		}

		if (dsend(&nh)) lamfail("lamshrink (dsend)");

		if (getnodeid() != nodetarget) {
			nh.nh_event = -getpid();
			nh.nh_length = 0;

			if (drecv(&nh)) lamfail("lamshrink (nrecv)");

			if (pdir->dir_reply != 0) {
				errno = pdir->dir_reply;
				lamfail("lamshrink (DIQREMLINK)");
			}
		}
	}

	if (fl_verbose) {
		nodespin_end();
	}

	kdetach(0);
/*
 * Kill the target node.
 */
	cmdn = 0;
	cmdv = 0;
	argvadd(&cmdn, &cmdv, DEFTTKILL);

	VERBOSE("%s ...\n", DEFTTKILL);

	if (nodetarget == getnodeid()) {
		r = _lam_few(cmdv);

		if (r) {
			errno = r;
		}
	} else {
		r = inetexec(hname, uname, cmdv, 
			     (fl_debug ? "lamshrink" : NULL));
	}

	argvfree(cmdv);

	if (r) {
		lamfail("lamshrink (tkill)");
	}

	free((char *) pnodes);
	return(0);
}

/*
 *	help
 *
 *	Function:	- prints usage information
 */
static void 
help()

{
	printf("Synopsis:\tlamshrink [options] <nodeid> <hostname>\n");
	printf("\nDescription:\tRemove a node from a LAM multicomputer.\n");
	printf("\nOptions:\t-h\t\tPrint this message.\n");
	printf("\t\t-d\t\tTurn on debugging / help mode.\n");
	printf("\t\t-v\t\tBe verbose.\n");
	printf("\t\t-u <userid>\tUse another userid.\n");
	printf("\t\t-w <secs>\tWarn target node processes & pause.\n");
	printf("\nNode:\t\tn<id>, eg., n5\n");
	printf("\nExample:\tlamshrink n3 host1\n");
	printf("\t\t\t\"Remove node 3, which is on host1.\"\n\n");
}
