#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <utmp.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/param.h>
#include <termios.h>
#include <mntent.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <syslog.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include "linux_reboot.h"
#include "pathnames.h"
#include "xstrncpy.h"
#include "nls.h"
static void usage(void), int_handler(int), write_user(struct utmp *);
static void wall(void), write_wtmp(void), unmount_disks(void);
static void unmount_disks_ourselves(void);
static void swap_off(void), do_halt(char *);
static void kill_mortals (int sig);
static void stop_finalprog (void);
static void syncwait (int timeval);
char *prog;
int opt_reboot;
int timeout;
int opt_quiet;
int opt_fast;
char message[90];
int opt_single = 0;
char *whom;
int opt_msgset = 0;
int opt_use_config_file = 1;
char halt_action[256];
#define WR(s) write(fd, s, strlen(s))
#define WRCRLF write(fd, "\r\n", 2)
#define ERRSTRING strerror(errno)
#define UMOUNT_ARGS "umount", "-a", "-t", "nodevfs"
#define SWAPOFF_ARGS "swapoff", "-a"
void
usage(void)
{
fprintf(stderr,
_("Usage: shutdown [-h|-r] [-fqs] [now|hh:ss|+mins]\n"));
exit(1);
}
static void
my_puts(char *s)
{
freopen(_PATH_CONSOLE, "w", stdout);
puts(s);
fflush(stdout);
}
void
int_handler(int sig)
{
unlink(_PATH_NOLOGIN);
signal(SIGINT, SIG_DFL);
my_puts(_("Shutdown process aborted"));
exit(1);
}
static int
iswhitespace(int a) {
return (a == ' ' || a == '\t');
}
int
main(int argc, char *argv[])
{
int c, i, fd;
char *ptr;
i = getdtablesize ();
for (fd = 3; fd < i; fd++) close (fd);
if (getpid () == 1)
{
for (fd = 0; fd < 3; fd++) close (fd);
while (1) wait (NULL);
}
sigsetmask (0);
for (i = 1; i < NSIG; i++) signal (i, SIG_DFL);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#ifndef DEBUGGING
if(setreuid (0, 0)) {
fprintf(stderr, _("%s: Only root can shut a system down.\n"),
argv[0]);
exit(1);
}
#endif
if(*argv[0] == '-') argv[0]++;
prog = argv[0];
if((ptr = strrchr(argv[0], '/'))) prog = ++ptr;
if(!strcmp("halt", prog)) {
opt_reboot = 0;
opt_quiet = 1;
opt_fast = 0;
timeout = 0;
} else if(!strcmp("fasthalt", prog)) {
opt_reboot = 0;
opt_quiet = 1;
opt_fast = 1;
timeout = 0;
} else if(!strcmp("reboot", prog)) {
opt_reboot = 1;
opt_quiet = 1;
opt_fast = 0;
timeout = 0;
} else if(!strcmp("fastboot", prog)) {
opt_reboot = 1;
opt_quiet = 1;
opt_fast = 1;
timeout = 0;
} else {
opt_reboot = 0;
opt_quiet = 0;
opt_fast = 0;
timeout = 2*60;
}
c = 0;
while(++c < argc) {
if(argv[c][0] == '-') {
for(i = 1; argv[c][i]; i++) {
switch(argv[c][i]) {
case 'C':
opt_use_config_file = 1;
break;
case 'h':
opt_reboot = 0;
break;
case 'r':
opt_reboot = 1;
break;
case 'f':
opt_fast = 1;
break;
case 'q':
opt_quiet = 1;
break;
case 's':
opt_single = 1;
break;
default:
usage();
}
}
} else if(!strcmp("now", argv[c])) {
timeout = 0;
} else if(argv[c][0] == '+') {
timeout = 60 * atoi(&argv[c][1]);
} else if (isdigit(argv[c][0])) {
char *colon;
int hour = 0;
int minute = 0;
time_t tics;
struct tm *tt;
int now, then;
if((colon = strchr(argv[c], ':'))) {
*colon = '\0';
hour = atoi(argv[c]);
minute = atoi(++colon);
} else usage();
(void) time(&tics);
tt = localtime(&tics);
now = 3600 * tt->tm_hour + 60 * tt->tm_min;
then = 3600 * hour + 60 * minute;
timeout = then - now;
if(timeout < 0) {
fprintf(stderr, _("That must be tomorrow, "
"can't you wait till then?\n"));
exit(1);
}
} else {
xstrncpy(message, argv[c], sizeof(message));
opt_msgset = 1;
}
}
halt_action[0] = 0;
if (opt_use_config_file) {
char line[256], *p;
FILE *fp;
halt_action[0] = '\0';
if ((fp = fopen (_PATH_SHUTDOWN_CONF, "r")) != NULL) {
if (fgets (line, sizeof(line), fp) != NULL &&
strncasecmp (line, "HALT_ACTION", 11) == 0 &&
iswhitespace(line[11])) {
p = index(line, '\n');
if (p)
*p = 0;
p = line+11;
while(iswhitespace(*p))
p++;
strcpy(halt_action, p);
}
fclose (fp);
}
}
if(!opt_quiet && !opt_msgset) {
int cnt = sizeof(message)-1;
char *ptr;
printf("Why? "); fflush(stdout);
ptr = message;
while(--cnt >= 0 && (*ptr = getchar()) && *ptr != '\n') {
ptr++;
}
*ptr = '\0';
} else if (!opt_msgset) {
strcpy(message, _("for maintenance; bounce, bounce"));
}
#ifdef DEBUGGING
printf("timeout = %d, quiet = %d, reboot = %d\n",
timeout, opt_quiet, opt_reboot);
#endif
if(!(whom = getlogin()) || !*whom) whom = "ghost";
if(strlen(whom) > 40) whom[40] = 0;
setpriority(PRIO_PROCESS, 0, PRIO_MIN);
signal(SIGINT, int_handler);
signal(SIGHUP, int_handler);
signal(SIGQUIT, int_handler);
signal(SIGTERM, int_handler);
chdir("/");
if(timeout > 5*60) {
sleep(timeout - 5*60);
timeout = 5*60;
}
if((fd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT, 0644)) >= 0) {
WRCRLF;
WR(_("The system is being shut down within 5 minutes"));
WRCRLF;
write(fd, message, strlen(message));
WRCRLF;
WR(_("Login is therefore prohibited."));
WRCRLF;
close(fd);
}
signal(SIGPIPE, SIG_IGN);
if(timeout > 0) {
wall();
sleep(timeout);
}
timeout = 0;
wall();
sleep(3);
signal(SIGINT, SIG_IGN);
openlog(prog, LOG_CONS, LOG_AUTH);
if (opt_reboot)
syslog(LOG_NOTICE, _("rebooted by %s: %s"),
whom, message);
else
syslog(LOG_NOTICE, _("halted by %s: %s"),
whom, message);
closelog();
if(opt_fast)
if((fd = open("/fastboot", O_WRONLY|O_CREAT, 0644)) >= 0)
close(fd);
kill(1, SIGTSTP);
write_wtmp();
if(opt_single)
if((fd = open(_PATH_SINGLE, O_CREAT|O_WRONLY, 0644)) >= 0)
close(fd);
sync();
signal(SIGTERM, SIG_IGN);
if(fork() > 0) sleep(1000);
setpgrp();
#ifndef DEBUGGING
kill_mortals (SIGTERM);
for (fd = 0; fd < 3; fd++) close (fd);
stop_finalprog ();
sleep (1);
kill (1, SIGTERM);
usleep (100000);
my_puts ("");
system ("/sbin/initctl -r");
syncwait (1);
my_puts ("Sending SIGTERM to all remaining processes...");
kill (-1, SIGTERM);
sleep (2);
kill (-1, SIGKILL);
acct(NULL);
#endif
unlink(_PATH_NOLOGIN);
kill (1, SIGQUIT);
sleep (1);
syncwait (2);
swap_off();
unmount_disks();
syncwait (1);
if(opt_reboot) {
my_reboot(LINUX_REBOOT_CMD_RESTART);
my_puts(_("\nWhy am I still alive after reboot?"));
} else {
my_puts(_("\nNow you can turn off the power..."));
my_reboot(LINUX_REBOOT_CMD_CAD_ON);
sleep (1);
do_halt(halt_action);
}
exit(0);
}
void
do_halt(char *action) {
if (strcasecmp (action, "power_off") == 0) {
printf(_("Calling kernel power-off facility...\n"));
fflush(stdout);
my_reboot(LINUX_REBOOT_CMD_POWER_OFF);
printf(_("Error powering off\t%s\n"), ERRSTRING);
fflush(stdout);
sleep (2);
} else
if (action[0] == '/') {
printf(_("Executing the program \"%s\" ...\n"), action);
fflush(stdout);
execl(action, action, NULL);
printf(_("Error executing\t%s\n"), ERRSTRING);
fflush(stdout);
sleep (2);
}
my_reboot(LINUX_REBOOT_CMD_HALT);
}
void
write_user(struct utmp *ut)
{
int fd;
int minutes, hours;
char term[40] = {'/','d','e','v','/',0};
char msg[100];
minutes = timeout / 60;
hours = minutes / 60;
minutes %= 60;
(void) strncat(term, ut->ut_line, sizeof(ut->ut_line));
if((fd = open(term, O_WRONLY|O_NONBLOCK)) < 0)
return;
msg[0] = '\007';
sprintf(msg+1, _("URGENT: broadcast message from %s:"), whom);
WRCRLF;
WR(msg);
WRCRLF;
if (hours > 1)
sprintf(msg, _("System going down in %d hours %d minutes"),
hours, minutes);
else if (hours == 1)
sprintf(msg, _("System going down in 1 hour %d minutes"),
minutes);
else if (minutes > 1)
sprintf(msg, _("System going down in %d minutes\n"),
minutes);
else if (minutes == 1)
sprintf(msg, _("System going down in 1 minute\n"));
else
sprintf(msg, _("System going down IMMEDIATELY!\n"));
WR(msg);
WRCRLF;
sprintf(msg, _("\t... %s ...\n"), message);
WR(msg);
WRCRLF;
close(fd);
}
void
wall(void)
{
struct utmp *ut;
utmpname(_PATH_UTMP);
setutent();
while((ut = getutent())) {
if(ut->ut_type == USER_PROCESS)
write_user(ut);
}
endutent();
}
void
write_wtmp(void)
{
int fd;
struct utmp ut;
memset((char *)&ut, 0, sizeof(ut));
strcpy(ut.ut_line, "~");
memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
time(&ut.ut_time);
ut.ut_type = BOOT_TIME;
if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0644)) >= 0) {
write(fd, (char *)&ut, sizeof(ut));
close(fd);
}
}
void
swap_off(void)
{
int pid;
int result;
int status;
sync();
if ((pid = fork()) < 0) {
my_puts(_("Cannot fork for swapoff. Shrug!"));
return;
}
if (!pid) {
execl("/sbin/swapoff", SWAPOFF_ARGS, NULL);
execl("/etc/swapoff", SWAPOFF_ARGS, NULL);
execl("/bin/swapoff", SWAPOFF_ARGS, NULL);
execlp("swapoff", SWAPOFF_ARGS, NULL);
my_puts(_("Cannot exec swapoff, "
"hoping umount will do the trick."));
exit(0);
}
while ((result = wait(&status)) != -1 && result != pid)
;
}
void
unmount_disks(void)
{
int pid;
int result;
int status;
sync();
if ((pid = fork()) < 0) {
my_puts(_("Cannot fork for umount, trying manually."));
unmount_disks_ourselves();
return;
}
if (!pid) {
execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
freopen(_PATH_CONSOLE, "w", stdout);
printf(_("Cannot exec %s, trying umount.\n"), _PATH_UMOUNT);
fflush(stdout);
execlp("umount", UMOUNT_ARGS, NULL);
my_puts(_("Cannot exec umount, giving up on umount."));
exit(0);
}
while ((result = wait(&status)) != -1 && result != pid)
;
my_puts(_("Unmounting any remaining filesystems..."));
unmount_disks_ourselves();
}
void
unmount_disks_ourselves(void)
{
FILE *mtab;
struct mntent *mnt;
char *mntlist[128];
int i;
int n;
char *filesys;
sync();
if (!(mtab = setmntent(_PATH_MOUNTED, "r"))) {
my_puts("shutdown: Cannot open " _PATH_MOUNTED ".");
return;
}
n = 0;
while (n < 100 && (mnt = getmntent(mtab))) {
if (strcmp(mnt->mnt_type, "devfs") == 0 ||
strcmp(mnt->mnt_type, "proc") == 0 ||
strcmp(mnt->mnt_type, "sysfs") == 0 ||
strcmp(mnt->mnt_type, "ramfs") == 0 ||
strcmp(mnt->mnt_type, "tmpfs") == 0 ||
strcmp(mnt->mnt_type, "devpts") == 0)
continue;
mntlist[n++] = strdup(mnt->mnt_dir);
}
endmntent(mtab);
for (i = n - 1; i >= 0; i--) {
filesys = mntlist[i];
#ifdef DEBUGGING
printf("umount %s\n", filesys);
#else
if (umount(mntlist[i]) < 0)
printf(_("shutdown: Couldn't umount %s: %s\n"),
filesys, ERRSTRING);
#endif
}
}
static void kill_mortals (int sig)
{
int npids = 0;
int index = 0;
int pid;
struct stat statbuf;
DIR *dp;
struct dirent *de;
pid_t *pids = NULL;
char path[256];
if ( ( dp = opendir ("/proc") ) == NULL ) return;
while ( ( de = readdir (dp) ) != NULL )
{
if ( !isdigit (de->d_name[0]) ) continue;
pid = atoi (de->d_name);
sprintf (path, "/proc/%d", pid);
if (stat (path, &statbuf) != 0) continue;
if (statbuf.st_uid < 100) continue;
if (index <= npids)
{
pids = realloc (pids, npids + 16384);
if (pids == NULL) return;
npids += 16384;
}
pids[index++] = pid;
}
fputs ("Sending SIGTERM to mortals...", stderr);
for (--index; index >= 0; --index) kill (pids[index], sig);
free (pids);
closedir (dp);
}
static void stop_finalprog (void)
{
char *p1, *p2;
FILE *fp;
char line[256];
if ( ( fp = fopen (_PATH_INITTAB, "r") ) == NULL ) return;
while (fgets (line, 256, fp) != NULL)
{
pid_t pid;
line[strlen (line) - 1] = '\0';
p1 = line;
while ( isspace (*p1) ) ++p1;
if (strncmp (p1, "finalprog", 9) != 0) continue;
if ( ( p1 = strchr (p1 + 9, '=') ) == NULL ) continue;
for (++p1; isspace (*p1); ++p1);
if (*p1 == '\0') continue;
for (p2 = p1; !isspace (*p2); ++p2);
*p2 = '\0';
switch ( pid = fork () )
{
case 0:
execl (p1, p1, "stop", NULL);
break;
case -1:
break;
default:
waitpid (pid, NULL, 0);
break;
}
fclose (fp);
return;
}
fclose (fp);
}
static void syncwait (int timeval)
{
static int do_wait = 0;
static int first_time = 1;
sync ();
if (first_time)
{
struct utsname uts;
first_time = 0;
uname (&uts);
if (uts.release[0] < '2') do_wait = 1;
}
if (do_wait) sleep (timeval);
}