/*
 * $QNXLicenseC:
 * Copyright 2007 - 2008, QNX Software Systems. All Rights Reserved.
 *
 * You must obtain a written license from and pay applicable 
 * license fees to QNX Software Systems before you may reproduce, 
 * modify or distribute this software, or any work that includes 
 * all or part of this software.   Free development licenses are 
 * available for evaluation and non-commercial purposes.  For more 
 * information visit http://licensing.qnx.com or email 
 * licensing@qnx.com.
 * 
 * This file may contain contributions from others.  Please review 
 * this entire file for other proprietary rights or license notices, 
 * as well as the QNX Development Suite License Guide at 
 * http://licensing.qnx.com/license-guide/ for other information.
 * $
 */

#ifdef __USAGE
%C - shut down and reboot the system (QNX)

%C	[-f] [-n node]
Options:
 -f     Shutdown fast. Reboot after only 1 second.
 -n     Node number to reboot. Default is current node.
 -b     Shut down but do not reboot. May not be used with -n node.
#endif

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/proc_msg.h>
#include <sys/dev.h>
#include <sys/vc.h>
#include <sys/sched.h>

//#include <util/stdutil.h>

#if 1
#include <sys/psinfo.h>
#include <sys/seginfo.h>
#include <sys/name.h>
int slay_fsys = 1;
#endif

#define strtonid qnx_strtonid
extern char    *nidtostr(nid_t nid, char *buf, int maxbuf);

main(argc, argv)
int argc;
char *argv[];
	{
	int count = 10, opt;
	nid_t node = 0;
	pid_t vid = PROC_PID;
	sigset_t bits;
	int noreboot = 0;
	char buf[8];
	int count2 = 60;

	union {
		struct _proc_shutdown		s;
		struct _proc_shutdown_reply	r;
		} msg;


	while ((opt = getopt(argc, argv, "fbn:")) != -1) {
		switch(opt) {
		case 'b': noreboot = 1; break;
		case 'f': count = 1; break;
		case 'n':
			node = strtonid(optarg,NULL);
			if (node==-1 || node == 0) {
				fprintf(stderr,"Invalid node specified (-n %s).\n",optarg);
				exit(EXIT_FAILURE);
			}
			break;

		default: exit(EXIT_FAILURE);
			}
		}

#if 0
	if(optind == argc  ||  strcmp(argv[optind], "please") != 0) {
		fprintf(stderr, "You must say please to reboot.\n");
		exit(EXIT_FAILURE);
		}
#endif

	if(node)
		if (noreboot) {
			fprintf(stderr,"shutdown: -b and -n options may not be used at the same time\n");
			exit(EXIT_FAILURE);
		}

		if((vid = qnx_vc_attach(node, PROC_PID, 100, 0)) == -1) {
			fprintf(stderr, "Unable to connect to node %s (%s).\n",nidtostr(node,NULL,0),strerror(errno));
			exit(EXIT_FAILURE);
			}

	bits = ~0L;
	sigprocmask(SIG_BLOCK, &bits, 0);

	if(vid == PROC_PID)
		putchar('\f');
	else
		printf("Please wait while remote node %s reboots...\n",nidtostr(node,NULL,0));
	fflush(stdout);
	display_msg(vid, "\n\n\n");	/* Move it down a few lines */

	msg.s.type = _PROC_SHUTDOWN;
	msg.s.signum = SIGPWR;
	errno = 0;
	Send(vid, &msg.s, &msg.r, sizeof(msg.s), sizeof(msg.r));

	if(errno  ||  (errno = msg.r.status)) {
		perror("Unable to shutdown");
		exit(EXIT_FAILURE);
		}

	if (!noreboot) {
		display_msg(vid, "\fStand by while system reboots\n");
	    }
	else {
		display_msg(vid, "\fStand by while system shuts down\n");
	}

	sleep(1);
	while(count >= 0) {
		sprintf(buf, "%2d   \r", count--);
		display_msg(vid, buf);
		sleep(1);
	}

#if 1
	if(slay_fsys) {
		pid_t pid;
		pid = qnx_name_locate( node, "qnx/fsys32", 1024, NULL );
		if(pid != -1) {
			struct _psinfo psdata;
			display_msg(vid, "\fStand by while filesystem shuts down\n");
			kill( pid, SIGTERM );
			if(node == 0) {
				while( qnx_psinfo( 0, pid, &psdata, 0, 0 ) == 0  &&  psdata.pid == pid ) {
					sprintf(buf, "%2d   \r", count2);
					display_msg(vid, buf);
					sleep(1);
					if(count2-- <= 0)
						break;
					}
				}
			}
		}
#endif

	if (!noreboot) {
		msg.s.type = _PROC_SHUTDOWN;
		msg.s.signum = -1;
		Send(vid, &msg.s, &msg.r, sizeof(msg.s), sizeof(msg.r));
		/* This should NOT return */
		}
	else {
		struct sched_param param;

		display_msg(vid, "\fshutdown: System may now be powered down.\n");
#if 1
		if(slay_fsys) {
			/*
			 * Return back to caller
			 */
			exit(EXIT_SUCCESS);
			}
#endif
		/* set priority way high */
		param.sched_priority = PRIO_FIFO_MAX;
		sched_setscheduler(0,SCHED_FIFO,&param);

		/* spin */
		for(;;);
		}
	}



void display_msg(vid, text)
pid_t vid;
char *text;
	{
	union {
		struct _proc_display		s;
		struct _proc_display_reply	r;
		} msg;

	msg.s.subtype = 0;
	strcpy(msg.s.text, text);
	msg.s.type = _PROC_DISPLAY;
	Send(vid, &msg.s, &msg.r, sizeof(msg.s), sizeof(msg.r));
	}
