blob: 2e10a586deec10c52cc935018e31562de0efcc4c [file] [log] [blame] [raw]
/*
* 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] [-h] [-i] [-p]
* -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.
* -h: put harddisks in standby mode
* -i: shut down all network interfaces.
* -p: power down the system (if possible, otherwise halt).
*
* Reboot and halt are both this program. Reboot
* is just a link to halt. Invoking the program
* as poweroff implies the -p option.
*
* Author: Miquel van Smoorenburg, miquels@cistron.nl
*
* Version: 2.86, 30-Jul-2004
*
* This file is part of the sysvinit suite,
* Copyright 1991-2004 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 <utmpx.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.86 31-Jul-2004 miquels@cistron.nl";
char *progname;
#define KERNEL_MONITOR 1 /* If halt() puts you into the kernel monitor. */
#define RUNLVL_PICKY 0 /* Be picky about the runlevel */
#define ALLOW_CALL_SHUTDOWN 1
#if !defined ALLOW_CALL_SHUTDOWN || !ALLOW_CALL_SHUTDOWN
#ifdef __linux__
#include <errno.h>
#elif defined __sun && defined __SVR4
#include <sys/uadmin.h>
#endif
#endif
#if defined __INTERIX || defined __FreeBSD__
#ifndef RUN_LVL
#ifdef EMPTY
#define RUN_LVL EMPTY
#else
#define RUN_LVL 0
#endif
#endif /* !RUN_LVL */
#endif
#if !defined ut_time && !defined __CYGWIN__
#define ut_time ut_tv.tv_sec
#endif
extern int ifdown(void);
extern int hddown(void);
extern void write_wtmp(char *user, char *id, int pid, int type, char *line);
/*
* Send usage message.
*/
void usage(void)
{
fprintf(stderr, "Usage: %s [-n] [-w] [-d] [-f] [-h] [-i]%s\n",
progname, strcmp(progname, "halt") ? "" : " [-p]");
fprintf(stderr, "\t-n: don't sync before halting the system\n");
fprintf(stderr, "\t-w: only write a wtmp reboot record and exit.\n");
fprintf(stderr, "\t-d: don't write a wtmp record.\n");
#if defined ALLOW_CALL_SHUTDOWN && ALLOW_CALL_SHUTDOWN
fprintf(stderr, "\t-f: force halt/reboot, don't call shutdown.\n");
#else
fprintf(stderr, "\t-f: try to do a fast reboot.\n");
#endif
fprintf(stderr, "\t-h: put harddisks in standby mode.\n");
fprintf(stderr, "\t-i: shut down all network interfaces.\n");
if (!strcmp(progname, "halt"))
fprintf(stderr, "\t-p: power down the system (if possible, otherwise halt).\n");
exit(1);
}
#if defined ALLOW_CALL_SHUTDOWN && ALLOW_CALL_SHUTDOWN
/*
* See if we were started directly from init.
* Get the runlevel from utmpx or the environment.
*/
int get_runlevel(void)
{
struct utmpx *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.
*/
setutxent();
while ((ut = getutxent()) != NULL) {
#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
}
endutxent();
/* This should not happen but warn the user! */
fprintf(stderr, "WARNING: could not determine runlevel"
" - doing soft %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("/usr/sbin/shutdown", args);
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(int argc, char **argv)
{
int do_reboot = 0;
int do_sync = 1;
int do_wtmp = 1;
int do_nothing = 0;
#if defined ALLOW_CALL_SHUTDOWN && ALLOW_CALL_SHUTDOWN
int do_hard = 0;
#else
int do_fast = 0;
#endif
int do_ifdown = 0;
int do_hddown = 0;
int do_poweroff = 0;
int c;
#if defined ALLOW_CALL_SHUTDOWN && ALLOW_CALL_SHUTDOWN
char *tm = NULL;
#endif
/*
* Find out who we are
*/
/* Remove dash passed on in argv[0] when used as login shell. */
if (argv[0][0] == '-') argv[0]++;
if ((progname = strrchr(argv[0], '/')) != NULL)
progname++;
else
progname = argv[0];
if (!strcmp(progname, "reboot")) do_reboot = 1;
if (!strcmp(progname, "poweroff")) do_poweroff = 1;
/*
* Get flags
*/
while((c = getopt(argc, argv, ":ihdfnpwt:")) != 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':
#if defined ALLOW_CALL_SHUTDOWN && ALLOW_CALL_SHUTDOWN
do_hard = 1;
#else
do_fast = 1;
#endif
break;
case 'i':
do_ifdown = 1;
break;
case 'h':
do_hddown = 1;
break;
case 'p':
do_poweroff = 1;
break;
case 't':
#if defined ALLOW_CALL_SHUTDOWN && ALLOW_CALL_SHUTDOWN
tm = optarg;
#endif
break;
default:
usage();
}
}
if (argc != optind) usage();
#ifndef __CYGWIN__
uid_t myeuid = geteuid();
#ifdef __INTERIX
if (myeuid != 66834 && myeuid != 197108 && myeuid != 1049076) {
#else
if (myeuid != 0) {
#endif
fprintf(stderr, "%s: must be superuser.\n", progname);
exit(1);
}
#endif
(void)chdir("/");
#if defined ALLOW_CALL_SHUTDOWN && ALLOW_CALL_SHUTDOWN
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)
write_wtmp("shutdown", "~~", 0, RUN_LVL, "~~");
/*
* Exit if all we wanted to do was write a wtmp record.
*/
if (do_nothing && !do_hddown && !do_ifdown) exit(0);
if (do_sync) {
#ifndef __INTERIX
sync();
#endif
sleep(2);
}
if (do_ifdown)
(void)ifdown();
if (do_hddown)
(void)hddown();
if (do_nothing) exit(0);
#if !defined __CYGWIN__ && !defined __INTERIX
if (do_reboot) {
#if !defined ALLOW_CALL_SHUTDOWN || !ALLOW_CALL_SHUTDOWN
if(do_fast) {
#ifdef __linux__
int fd = open("/sys/kernel/kexec_loaded", O_RDONLY);
if(fd != -1) {
char val;
int s = read(fd, &val, 1);
close(fd);
if(s == 1) {
if(val == '0') {
fprintf(stderr, "%s: no kernel loaded, ", progname);
} else {
init_reboot(BMAGIC_KEXEC);
fprintf(stderr, "%s: kexec failed: %s; ",
progname, strerror(errno));
}
} else {
fprintf(stderr, "%s: cannot read ''; ", progname);
}
} else fprintf(stderr, "%s: cannot open '': %s; ", progname, strerror(errno));
fprintf(stderr, "doing normal reboot\n");
#elif defined __sun && defined __SVR4 && defined AD_FASTREBOOT
// Should we replace init_reboot with uadmin?
uadmin(A_SHUTDOWN, AD_FASTREBOOT, NULL);
#endif
}
#endif /* !ALLOW_CALL_SHUTDOWN */
init_reboot(BMAGIC_REBOOT);
} else {
/*
* Turn on hard reboot, CTRL-ALT-DEL will reboot now
*/
#ifdef BMAGIC_HARD
init_reboot(BMAGIC_HARD);
#endif
/*
* Stop init; it is insensitive to the signals sent
* by the kernel.
*/
kill(1, SIGTSTP);
/*
* Halt or poweroff.
*/
if (do_poweroff)
init_reboot(BMAGIC_POWEROFF);
/*
* Fallthrough if failed.
*/
init_reboot(BMAGIC_HALT);
}
/*
* If we return, we (c)ontinued from the kernel monitor.
*/
#ifdef BMAGIC_SOFT
init_reboot(BMAGIC_SOFT);
#endif
kill(1, SIGCONT);
#endif /* !__CYGWIN__ && !__INTERIX */
exit(0);
}