sysvinit/src/halt.c

334 lines
7.5 KiB
C

/*
* 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 (C) 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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"
#include "runlevellog.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 */
extern int ifdown(void);
extern int hddown(void);
extern int hdflush(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");
fprintf(stderr, "\t-f: force halt/reboot, don't call shutdown.\n");
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);
}
/*
* See if we were started directly from init.
* Get the runlevel from /var/run/utmp or the environment.
* Or the /var/run/runlevel log.
*/
int get_runlevel(void)
{
struct utmp *ut;
char *r;
int runlevel, status;
#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.
*/
setutent();
while ((ut = getutent()) != 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
}
endutent();
/* Did not find utmp entry, try to read from log file */
status = Read_Runlevel_Log(&runlevel);
if (status)
return runlevel;
/* 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, int should_poweroff, char *tm)
{
char *args[9];
int i = 0;
args[i++] = "shutdown";
args[i++] = fl;
if ( (! strcmp(fl, "-h") ) && (should_poweroff) )
args[i++] = "-P";
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);
}
/*
* 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;
int do_hard = 0;
int do_ifdown = 0;
int do_hddown = 0;
int do_poweroff = 0;
int c;
char *tm = NULL;
/*
* 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':
do_hard = 1;
break;
case 'i':
do_ifdown = 1;
break;
case 'h':
do_hddown = 1;
break;
case 'p':
do_poweroff = 1;
break;
case 't':
tm = optarg;
break;
default:
usage();
}
}
if (argc != optind) usage();
if (geteuid() != 0) {
fprintf(stderr, "%s: must be superuser.\n", progname);
exit(1);
}
if (chdir("/")) {
fprintf(stderr, "%s: chdir(/): %m\n", progname);
exit(1);
}
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", do_poweroff, tm);
}
/*
* 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) {
sync();
/* Sync should be fine on its own for making sure data is written.
We probably call shutdown after this anyway to clean up.
-- Jesse
sleep(2);
*/
}
if (do_ifdown)
(void)ifdown();
if (do_hddown)
(void)hddown();
else
(void)hdflush();
if (do_nothing) exit(0);
if (do_reboot) {
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);
exit(0);
}