| /* |
| * 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 wtmpx reboot record and exit. |
| * -d: don't write a wtmpx 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 <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 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 wtmpx reboot record and exit.\n"); |
| fprintf(stderr, "\t-d: don't write a wtmpx 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("/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(); |
| else |
| (void)hdflush(); |
| |
| 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 '/sys/kernel/kexec_loaded'; ", progname); |
| } |
| } else fprintf(stderr, "%s: cannot open '/sys/kernel/kexec_loaded': %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); |
| } |