/*
 * Halt		Stop the system running.
 *		It re-enables CTRL-ALT-DEL, so that a hard reboot can
 *		be done. If called as reboot, it will reboot the system.
 *
 *		If the system is not in runlevel 0 or 6, halt will just
 *		execute a "shutdown -h" to halt the system, and reboot will
 *		execute an "shutdown -r". This is for compatibility with
 *		sysvinit 2.4.
 *
 * Usage:	halt [-n] [-w] [-d] [-f]
 *		-n: don't sync before halting the system
 *		-w: only write a wtmp reboot record and exit.
 *		-d: don't write a wtmp record.
 *		-f: force halt/reboot, don't call shutdown.
 *
 *		Reboot and halt are both this program. Reboot
 *		is just a link to halt.
 *
 * Author:	Miquel van Smoorenburg, miquels@drinkel.cistron.nl
 *
 * Version:	2.3,  13-Aug-1996
 *
 *		This file is part of the sysvinit suite,
 *		Copyright 1991-1996 Miquel van Smoorenburg.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdlib.h>
#include <utmp.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/times.h>
#include <time.h>
#include <signal.h>
#include <stdio.h>
#include <getopt.h>
#include "reboot.h"

char *Version = "@(#)halt 2.3 13-Aug-1996 MvS";
char *progname;

#define COMPATIBLE 1	  /* For compatibility with sysvinit 2.4 */
#define KERNEL_MONITOR	1 /* If halt() puts you into the kernel monitor. */
#define RUNLVL_PICKY	0 /* Be picky about the runlevel */

/*
 * Send usage message.
 */
void usage()
{
  fprintf(stderr, "usage: %s [-n] [-w] [-d] [-f]\n", progname);
  exit(1);
}

#if COMPATIBLE
/* See if we were started directly from init. */

/* Get the runlevel from /var/run/utmp. */
int get_runlevel()
{
  FILE *fp;
  struct utmp ut;
  char *r;
#if RUNLVL_PICKY
  time_t boottime;
#endif

  /* First see if we were started directly from init. */
  if (getenv("INIT_VERSION") && (r = getenv("RUNLEVEL")) != NULL)
	return(*r);

  /* Hmm, failed - read runlevel from /var/run/utmp.. */

#if RUNLVL_PICKY
  /* Get boottime from the kernel. */
  time(&boottime);
  boottime -= (times(NULL) / HZ);
#endif

  /* Find runlevel in utmp, */
  if ((fp = fopen(UTMP_FILE, "r")) != NULL) {
	while (fread(&ut, sizeof(struct utmp), 1, fp) == 1)
#if RUNLVL_PICKY
		/* Only accept value if it's from after boottime. */
		if (ut.ut_type == RUN_LVL && ut.ut_time > boottime)
			return(ut.ut_pid & 255);
#else
		if (ut.ut_type == RUN_LVL)
			return(ut.ut_pid & 255);
#endif
  }
  if (fp) fclose(fp);

  /* This should not happen but warn the user! */
  fprintf(stderr, "WARNING: could not determine runlevel - doing hard %s\n",
		progname);
  fprintf(stderr, "  (it's better to use shutdown instead of %s from the command line)\n",
		progname);
  return(-1);
}

/* Switch to another runlevel. */
void do_shutdown(char *fl, char *tm)
{
  char *args[8];
  int i = 0;

  args[i++] = "shutdown";
  args[i++] = fl;
  if (tm) {
	args[i++] = "-t";
	args[i++] = tm;
  }
  args[i++] = "now";
  args[i++] = NULL;

  execv("/sbin/shutdown", args);
  execv("/etc/shutdown", args);
  execv("/bin/shutdown", args);
  perror("shutdown");
  exit(1);
}
#endif

/*
 * Main program.
 * Write a wtmp entry and reboot cq. halt.
 */
int main(argc, argv)
int argc;
char **argv;
{
  struct utmp wtmp;
  int fd;
  int do_reboot = 0;
  int do_sync = 1;
  int do_wtmp = 1;
  int do_nothing = 0;
  int do_hard = 0;
  int c;
#if COMPATIBLE
  char *tm = NULL;
#endif

  /* Find out who we are */
  if ((progname = strrchr(argv[0], '/')) != NULL)
  	progname++;
  else
  	progname = argv[0];
  	
  if (geteuid() != 0) {
	fprintf(stderr, "%s: must be superuser.\n", progname);
	exit(1);
  }

  if (!strcmp(progname, "reboot")) do_reboot = 1;

  /* Get flags */
  while((c = getopt(argc, argv, ":dfnwt:")) != EOF) {
	switch(c) {
		case 'n':
			do_sync = 0;
			do_wtmp = 0;
			break;
		case 'w':
			do_nothing = 1;
			break;
		case 'd':
			do_wtmp = 0;
			break;
		case 'f':
			do_hard = 1;
			break;
#if COMPATIBLE
		case 't':
			tm = optarg;
			break;
#endif
		default:
			usage();
	}
  }
  if (argc != optind) usage();

#if COMPATIBLE
  if (!do_hard && !do_nothing) {
	/* See if we are in runlevel 0 or 6. */
	c = get_runlevel();
	if (c > '0' && c < '6')
		do_shutdown(do_reboot ? "-r" : "-h", tm);
  }
#endif

  /* Record the fact that we're going down */
  if (do_wtmp && (fd = open(WTMP_FILE, O_WRONLY|O_APPEND)) >= 0) {
  	strcpy(wtmp.ut_user, "shutdown");
  	strcpy(wtmp.ut_line, "~");
  	strcpy(wtmp.ut_id,  "~~");
  	wtmp.ut_pid = 0;
  	wtmp.ut_type = RUN_LVL;
#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ >= 6
	gettimeofday(&wtmp.ut_tv, NULL);
#else
	time(&wtmp.ut_time);     
#endif                                                
  	write(fd, (char *)&wtmp, sizeof(wtmp));
  	close(fd);
  }

  /* Exit if all we wanted to do was write a wtmp record. */
  if (do_nothing) exit(0);

  if (do_sync) {
	sync();
	sleep(2);
  }

  if (do_reboot) {
	init_reboot(BMAGIC_REBOOT);
  } else {
	/* Turn on hard reboot, CTRL-ALT-DEL will reboot now */
	init_reboot(BMAGIC_HARD);

	/* Stop init; it is insensitive to the signals sent by the kernel. */
	kill(1, SIGTSTP);

	/* And perform the `halt' system call. */
	init_reboot(BMAGIC_HALT);
  }
  /* If we return, we (c)ontinued from the kernel monitor. */
  init_reboot(BMAGIC_SOFT);
  kill(1, SIGCONT);

  exit(0);
}
