/*
 * shutdown.c	- Shut down the system.
 *
 * Usage:	  shutdown [-krhnf] time [warning message]
 *		  -k: don't really shutdown, only warn.
 *		  -r: reboot after shutdown.
 *		  -h: halt after shutdown.
 *		  -n: don't sync before reboot or halt.
 *		  -f: do a 'fast' reboot.
 *
 * Author:	  Miquel van Smoorenburg, miquels@drinkel.nl.mugnet.org
 *
 */
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h> 
#include <signal.h>
#include <fcntl.h>

char *Version = "@(#) shutdown 1.1 18-11-1992 MvS";

int dontshut = 0;	/* Don't shutdown, only warn	*/
int do_reboot = 0;	/* Reboot after shutdown	*/
int halt = 0;		/* Halt after shutdown		*/
int dosync = 1;		/* Sync before reboot or halt	*/
int fastboot = 0;	/* Do a 'fast' reboot		*/
char reason[256];	/* Warning message		*/

/* From "wall.c" */
extern void wall();

/*
 * Break off an already running shutdown.
 */
void stopit()
{
  unlink("/etc/nologin");
  unlink("/etc/fastboot");
  printf("\nShutdown cancelled.\n");
  exit(0);
}

/*
 * Show usage message.
 */
void usage()
{
 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n",
	"Usage:\t  shutdown [-krhnf] time [warning message]",
	"\t\t  -k: don't really shutdown, only warn.",
	"\t\t  -r: reboot after shutdown.",
	"\t\t  -h: halt after shutdown.",
	"\t\t  -n: don't sync before reboot or halt.",
	"\t\t  -f: do a 'fast' reboot.");
  exit(1);
}

/*
 * Tell everyone the system is going down in 'mins' minutes.
 */
void warn(mins)
int mins;
{
  char buf[512];
  
  sprintf(buf, "\rThe system is going DOWN in %d minutes !!\r\n", mins);
  if (reason[0]) strcat(buf, reason);
  wall(buf);
}

/*
 * Create the /etc/nologin file.
 */
void donologin()
{
  FILE *fp;
  time_t t;

  time(&t);
  t += 300;

  unlink("/etc/nologin");
  if (fp = fopen("/etc/nologin", "w")) {
  	fprintf(fp, "\rThe system is going down on %s\r", ctime(&t));
  	if (reason[0]) fputs(reason, fp);
  	fclose(fp);
  }
}

/*
 * Execute /etc/reboot or /etc/halt.
 */
void shutdown()
{
  char *prog = (char *)0;
  char *args[3];

  wall("\r\nThe system is going down NOW\r\n");
  if (dontshut) stopit();

  if (do_reboot) prog = "/etc/reboot";
  if (halt) prog = "/etc/halt";

  args[0] = prog;
  if (dosync)
  	args[1] = (char *)NULL;
  else {
  	args[1] = "-n";
  	args[2] = (char *)NULL;
  }

  unlink("/etc/nologin");

  if (prog) {
  	execv(prog, args);
  	fprintf(stderr, "shutdown: cannot execute %s\n", prog);
	unlink("/etc/fastboot");
	exit(1);
  }
  if (dosync) sync();
  execl("/etc/init", "telinit", "s", NULL);
  fprintf(stderr, "shutdown: cannot get into single user mode\n");
  unlink("/etc/fastboot");	
  exit(1);
}

/*
 * Main program.
 * Process the options and do the final countdown.
 */
int main(argc, argv)
int argc;
char **argv;
{
  extern int getopt();
  extern int optind; 
  int c, wt, hours, mins;
  struct tm *lt;
  time_t t;
  char *n;
  int didnolog = 0;

  if (getuid() != 0) {
  	fprintf(stderr, "shutdown: must be root.\n");
  	exit(1);
  }

  while((c = getopt(argc, argv, "krhnf")) != EOF) {
  	switch(c) {
  		case 'k': /* Don't really shutdown, only warn.*/
  			dontshut = 1;
  			break;
  		case 'r': /* Automatic reboot */
  			do_reboot = 1;
  			halt = 0;
  			break;
  		case 'h': /* Halt after shutdown */
  			halt = 1;
  			do_reboot = 0;
  			break;
  		case 'n': /* Don't sync after shutdown */
  			dosync = 0;
  			break;
  		case 'f': /* Don't perform fsck after next boot */
  			fastboot = 1;
  			break;
  		default:
  			usage();
  			break;	
  	}
  }
  if (optind == argc) usage();
  n = argv[optind++];
  
  reason[0] = 0;
  for(c = optind; c < argc; c++) {
  	strcat(reason, argv[c]);
  	strcat(reason, " ");
  }
  if (reason[0]) strcat(reason, "\r\n");

  for(c = 1; c < _NSIG; c++) signal(c, SIG_IGN);
  signal(SIGINT, stopit);

  /* Go to the root directory */
  chdir("/");
  if (fastboot) close(open("/etc/fastboot", O_CREAT | O_RDWR, 0644));

  if (!strcmp(n, "now") || !strcmp(n, "+0")) shutdown();

  if (n[0] == '+') {
  	wt = atoi(n + 1);
  	if (wt == 0) usage();
  } else {
  	if (sscanf(n, "%d:%2d", &hours, &mins) != 2) usage();
  	if (hours > 23 || mins > 59) usage();
  	time(&t);
  	lt = localtime(&t);
  	wt = (60*hours + mins) - (60*lt->tm_hour + lt->tm_min);
  	if (wt < 0) wt += 1440;
  }
  if (wt < 15 && wt != 5 && wt != 1) warn(wt);

  while(wt) {
  	if (wt <= 5 && !didnolog) {
  		donologin();
  		didnolog++;
  	}
  	if (wt == 15 || wt == 10 || wt == 5 || wt == 1) warn(wt);
  	sleep(60);
  	wt--;
  }
  shutdown();
  return(0); /* Never happens */
}
