Add halt(8), poweroff(8), and reboot(8).

This commit is contained in:
Jonas 'Sortie' Termansen 2021-12-11 03:28:59 +01:00
parent f2d50bbf9c
commit 71edc766e7
17 changed files with 281 additions and 74 deletions

View File

@ -266,11 +266,14 @@ own log.
.Sh ASYNCHRONOUS EVENTS .Sh ASYNCHRONOUS EVENTS
.Bl -tag -width "SIGUSR1" .Bl -tag -width "SIGUSR1"
.It Dv SIGTERM .It Dv SIGTERM
Request system poweroff. Request system poweroff, normally sent by
.Xr poweroff 8 .
.It Dv SIGINT .It Dv SIGINT
Request system reboot. Request system reboot, normally sent by
.Xr reboot 8 .
.It Dv SIGQUIT .It Dv SIGQUIT
Request system halt. Request system halt, normally sent by
.Xr halt 8 .
.El .El
.Sh EXIT STATUS .Sh EXIT STATUS
.Nm .Nm
@ -292,7 +295,10 @@ exits with the same exit status as its target session if it terminates normally.
.Xr daemon 7 , .Xr daemon 7 ,
.Xr initrd 7 , .Xr initrd 7 ,
.Xr kernel 7 , .Xr kernel 7 ,
.Xr halt 8 ,
.Xr login 8 , .Xr login 8 ,
.Xr poweroff 8 ,
.Xr reboot 8 ,
.Xr sysmerge 8 , .Xr sysmerge 8 ,
.Xr update-initrd 8 .Xr update-initrd 8
.Sh SECURITY CONSIDERATIONS .Sh SECURITY CONSIDERATIONS

View File

@ -636,6 +636,8 @@ static void keyboard_event(struct glogin* state, uint32_t codepoint)
exit(0); exit(0);
if ( !strcmp(textbox_username.text, "reboot") ) if ( !strcmp(textbox_username.text, "reboot") )
exit(1); exit(1);
if ( !strcmp(textbox_username.text, "halt") )
exit(2);
state->stage = STAGE_PASSWORD; state->stage = STAGE_PASSWORD;
textbox_reset(&textbox_password); textbox_reset(&textbox_password);
break; break;

View File

@ -44,6 +44,8 @@ alias for poweroff
exit asking for powering off the computer exit asking for powering off the computer
.It reboot .It reboot
exit asking for rebooting the computer exit asking for rebooting the computer
.It halt
exit asking for halting the computer
.El .El
.Sh SECURITY .Sh SECURITY
There is currently no method to confirm the login screen is in fact real other There is currently no method to confirm the login screen is in fact real other

View File

@ -349,6 +349,8 @@ int textual(void)
exit(0); exit(0);
if ( !strcmp(username, "reboot") ) if ( !strcmp(username, "reboot") )
exit(1); exit(1);
if ( !strcmp(username, "halt") )
exit(2);
if ( settermmode(0, pw_termmode) < 0 ) if ( settermmode(0, pw_termmode) < 0 )
err(2, "settermmode"); err(2, "settermmode");

View File

@ -357,8 +357,9 @@ if applicable restore boot priorities in your firmware.
If you did not accept the bootloader, you will need to manually configure a If you did not accept the bootloader, you will need to manually configure a
bootloader to boot the new operating system. bootloader to boot the new operating system.
.Pp .Pp
You will be given the choice between powering off the system, rebooting it, or You will be given the choice between powering off the system, rebooting it,
directly booting the new system. halting it, or directly booting the new system.
.Pp
The last option will directly boot the new system in a chroot while the live The last option will directly boot the new system in a chroot while the live
environment remains in the background. environment remains in the background.
If you invoked If you invoked
@ -369,10 +370,17 @@ Otherwise the computer will power off when the chroot environment terminates.
Upon boot of the new system it will be configured in multi-user mode and you Upon boot of the new system it will be configured in multi-user mode and you
will be presented with a login screen. will be presented with a login screen.
Authenticate as one of the local users and you will be given a shell. Authenticate as one of the local users and you will be given a shell.
.Pp
To power off the computer login as user To power off the computer login as user
.Sy poweroff .Sy poweroff
and to reboot the computer login as user or run
.Sy reboot . .Xr poweroff 8
after logging in.
To reboot the computer login as user
.Sy reboot
or run
.Xr reboot 8
after logging in.
.Pp .Pp
The The
.Xr user-guide 7 .Xr user-guide 7

View File

@ -24,17 +24,16 @@ This is
if the system is booted in multi-user mode. if the system is booted in multi-user mode.
This is a root shell if booted in single-user mode. This is a root shell if booted in single-user mode.
.Pp .Pp
To power off from the login screen, login as user To power off the computer login as user
.Sy poweroff . .Sy poweroff
To reboot, login as user or run
.Sy reboot . .Xr poweroff 8
.Pp after logging in.
To power off from a single-user boot root shell, run To reboot the computer login as user
.Sy exit 0 .Sy reboot
in the shell. or run
To reboot, run .Xr reboot 8
.Sy exit 1 after logging in.
in the shell.
.Ss Keyboard Layout .Ss Keyboard Layout
The kernel has a default US keyboard layout compiled into it. The kernel has a default US keyboard layout compiled into it.
.Pp .Pp

View File

@ -828,22 +828,24 @@ int main(void)
while ( true ) while ( true )
{ {
prompt(input, sizeof(input), prompt(input, sizeof(input),
"Install " BRAND_DISTRIBUTION_NAME "? (yes/no/poweroff/reboot)", "Install " BRAND_DISTRIBUTION_NAME "? "
"yes"); "(yes/no/poweroff/reboot/halt)", "yes");
if ( !strcasecmp(input, "yes") ) if ( !strcasecmp(input, "yes") )
break; break;
else if ( !strcasecmp(input, "no") ) else if ( !strcasecmp(input, "no") )
{ {
text("Answer '!' to get a shell. Type !man to view the " text("Answer '!' to get a shell. Type !man to view the "
"installation(7) manual page.\n"); "installation(7) manual page.\n");
text("Alternatively, you can answer 'poweroff' or 'reboot' to " text("Alternatively, you can answer 'poweroff', 'reboot', or "
"cancel the installation.\n"); "'halt' to cancel the installation.\n");
continue; continue;
} }
else if ( !strcasecmp(input, "poweroff") ) else if ( !strcasecmp(input, "poweroff") )
exit(0); exit(0);
else if ( !strcasecmp(input, "reboot") ) else if ( !strcasecmp(input, "reboot") )
exit(1); exit(1);
else if ( !strcasecmp(input, "halt") )
exit(2);
else else
continue; continue;
} }
@ -1133,17 +1135,21 @@ int main(void)
text("Upon boot, you'll be greeted with a login screen. Enter your " text("Upon boot, you'll be greeted with a login screen. Enter your "
"credentials to get a command line. Login as user 'poweroff' as " "credentials to get a command line. Login as user 'poweroff' as "
"described in login(8) to power off the machine. After logging in, " "described in login(8) to power off the machine or run poweroff(8). "
"type 'man user-guide' to view the introductory documentation.\n"); "After logging in, type 'man user-guide' to view the introductory "
"documentation.\n");
text("\n"); text("\n");
while ( true ) while ( true )
{ {
prompt(input, sizeof(input), "What now? (poweroff/reboot/boot)", "boot"); prompt(input, sizeof(input),
"What now? (poweroff/reboot/halt/boot)", "boot");
if ( !strcasecmp(input, "poweroff") ) if ( !strcasecmp(input, "poweroff") )
exit(0); exit(0);
if ( !strcasecmp(input, "reboot") ) if ( !strcasecmp(input, "reboot") )
exit(1); exit(1);
if ( !strcasecmp(input, "halt") )
exit(2);
if ( !strcasecmp(input, "boot") ) if ( !strcasecmp(input, "boot") )
{ {
unmount_all_but_root(); unmount_all_but_root();

View File

@ -775,7 +775,7 @@ int main(void)
while ( true ) while ( true )
{ {
promptx(input, sizeof(input), promptx(input, sizeof(input),
"Upgrade? (yes/no/poweroff/reboot)", "yes", true); "Upgrade? (yes/no/poweroff/reboot/halt)", "yes", true);
if ( !strcasecmp(input, "yes") ) if ( !strcasecmp(input, "yes") )
break; break;
else if ( !strcasecmp(input, "no") ) else if ( !strcasecmp(input, "no") )
@ -784,14 +784,16 @@ int main(void)
"upgrade(7) manual page. You can edit the upgrade.conf(5) " "upgrade(7) manual page. You can edit the upgrade.conf(5) "
"configuration file of the target system to change which " "configuration file of the target system to change which "
"upgrade operations are performed.\n"); "upgrade operations are performed.\n");
text("Alternatively, you can answer 'poweroff' or 'reboot' to " text("Alternatively, you can answer 'poweroff', 'reboot', or "
"cancel the upgrade.\n"); "'halt' or cancel the upgrade.\n");
continue; continue;
} }
else if ( !strcasecmp(input, "poweroff") ) else if ( !strcasecmp(input, "poweroff") )
exit(0); exit(0);
else if ( !strcasecmp(input, "reboot") ) else if ( !strcasecmp(input, "reboot") )
exit(1); exit(1);
else if ( !strcasecmp(input, "halt") )
exit(2);
else if ( !strcasecmp(input, "!") ) else if ( !strcasecmp(input, "!") )
break; break;
else else
@ -928,10 +930,13 @@ int main(void)
while ( true ) while ( true )
{ {
prompt(input, sizeof(input), "What now? (poweroff/reboot)", "reboot"); prompt(input, sizeof(input),
"What now? (poweroff/reboot/halt)", "reboot");
if ( !strcasecmp(input, "poweroff") ) if ( !strcasecmp(input, "poweroff") )
return 0; return 0;
if ( !strcasecmp(input, "reboot") ) if ( !strcasecmp(input, "reboot") )
return 1; return 1;
if ( !strcasecmp(input, "halt") )
return 2;
} }
} }

5
utils/.gitignore vendored
View File

@ -20,9 +20,10 @@ env
expr expr
false false
find find
halt
head head
id
help help
id
kernelinfo kernelinfo
kill kill
ln ln
@ -35,11 +36,13 @@ mv
nl nl
pager pager
passwd passwd
poweroff
ps ps
pstree pstree
pwd pwd
readlink readlink
realpath realpath
reboot
rm rm
rmdir rmdir
sleep sleep

View File

@ -31,6 +31,7 @@ env \
expr \ expr \
false \ false \
find \ find \
halt \
head \ head \
help \ help \
id \ id \
@ -71,6 +72,8 @@ uptime \
wc \ wc \
which \ which \
yes \ yes \
poweroff \
reboot \
BINARIES=\ BINARIES=\
$(BINARIES_EXCEPT_INSTALL) \ $(BINARIES_EXCEPT_INSTALL) \
@ -94,6 +97,9 @@ unmount \
MANPAGES8=\ MANPAGES8=\
chroot.8 \ chroot.8 \
halt.8 \
poweroff.8 \
reboot.8 \
unmount.8 \ unmount.8 \
all: $(BINARIES) $(SBINS) all: $(BINARIES) $(SBINS)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013, 2015, 2016 Jonas 'Sortie' Termansen. * Copyright (c) 2013, 2015, 2016, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2021 Juhani 'nortti' Krekelä. * Copyright (c) 2021 Juhani 'nortti' Krekelä.
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
@ -27,49 +27,11 @@
#define ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) #define ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0]))
static const char* tty_name(void)
{
int tty_fd = open("/dev/tty", O_RDONLY);
const char* result = NULL;
if ( 0 <= tty_fd )
{
result = ttyname(tty_fd);
close(tty_fd);
}
return result ? result : "/dev/tty";
}
static void suggest_logout(void) static void suggest_logout(void)
{ {
fprintf(stderr, " Exiting your shell normally to logout.\n"); fprintf(stderr, " Exiting your shell normally to logout.\n");
} }
static void suggest_poweroff(void)
{
if ( strcmp(tty_name(), "/dev/tty1") != 0 )
fprintf(stderr, " Powering off on /dev/tty1.\n");
else if ( getenv("LOGIN_PID") )
{
fprintf(stderr, " Exiting your shell normally to logout.\n");
fprintf(stderr, " Login as user 'poweroff' to power off computer.\n");
}
else
fprintf(stderr, " Exiting your shell normally to poweroff.\n");
}
static void suggest_reboot(void)
{
if ( strcmp(tty_name(), "/dev/tty1") != 0 )
fprintf(stderr, " Rebooting on /dev/tty1.\n");
else if ( getenv("LOGIN_PID") )
{
fprintf(stderr, " Exiting your shell normally to logout.\n");
fprintf(stderr, " Login as user 'reboot' to reboot computer.\n");
}
else
fprintf(stderr, " Exiting your shell with 'exit 1' to reboot.\n");
}
enum category enum category
{ {
NONE, NONE,
@ -79,7 +41,6 @@ enum category
MOUNT, MOUNT,
PAGER, PAGER,
POWEROFF, POWEROFF,
REBOOT,
RW, RW,
SHELL, SHELL,
UNMOUNT, UNMOUNT,
@ -122,12 +83,9 @@ struct command commands[] =
{PAGER, "more", NULL, NULL}, {PAGER, "more", NULL, NULL},
{PAGER, "pager", "system", NULL}, {PAGER, "pager", "system", NULL},
{POWEROFF, "halt", NULL, NULL}, {POWEROFF, "poweroff", "system", NULL},
{POWEROFF, "poweroff", NULL, suggest_poweroff},
{POWEROFF, "shutdown", NULL, NULL}, {POWEROFF, "shutdown", NULL, NULL},
{REBOOT, "reboot", NULL, suggest_reboot},
{RW, "dd", NULL, NULL}, {RW, "dd", NULL, NULL},
{RW, "rw", "system", NULL}, {RW, "rw", "system", NULL},

20
utils/halt.8 Normal file
View File

@ -0,0 +1,20 @@
.Dd December 13, 2021
.Dt HALT 8
.Os
.Sh NAME
.Nm halt
.Nd halt the computer
.Sh SYNOPSIS
.Nm
.Sh DESCRIPTION
.Nm
gracefully halts the computer by sending a request to the appropriate
.Xr init 8
process.
.Sh EXIT STATUS
.Nm
will exit 0 on success and non-zero otherwise.
.Sh SEE ALSO
.Xr init 8 ,
.Xr poweroff 8 ,
.Xr reboot 8

50
utils/halt.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* halt.c
* Halts the computer.
*/
#include <sys/types.h>
#include <err.h>
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int opt;
while ( (opt = getopt(argc, argv, "")) != -1 )
{
switch ( opt )
{
default: return 1;
}
}
if ( optind < argc )
errx(1, "extra operand: %s", argv[optind]);
pid_t init_pid = 1;
// TODO: Use a more reliable getinit() approach that also works in sshd.
if ( getenv("INIT_PID") )
init_pid = atoll(getenv("INIT_PID"));
if ( kill(init_pid, SIGQUIT) < 0 )
err(1, "kill: %" PRIdPID, init_pid);
return 0;
}

20
utils/poweroff.8 Normal file
View File

@ -0,0 +1,20 @@
.Dd December 13, 2021
.Dt POWEROFF 8
.Os
.Sh NAME
.Nm poweroff
.Nd power off the computer
.Sh SYNOPSIS
.Nm
.Sh DESCRIPTION
.Nm
gracefully powers off the computer by sending a request to the appropriate
.Xr init 8
process.
.Sh EXIT STATUS
.Nm
will exit 0 on success and non-zero otherwise.
.Sh SEE ALSO
.Xr halt 8 ,
.Xr init 8 ,
.Xr reboot 8

50
utils/poweroff.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* poweroff.c
* Powers off the computer.
*/
#include <sys/types.h>
#include <err.h>
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int opt;
while ( (opt = getopt(argc, argv, "")) != -1 )
{
switch ( opt )
{
default: return 1;
}
}
if ( optind < argc )
errx(1, "extra operand: %s", argv[optind]);
pid_t init_pid = 1;
// TODO: Use a more reliable getinit() approach that also works in sshd.
if ( getenv("INIT_PID") )
init_pid = atoll(getenv("INIT_PID"));
if ( kill(init_pid, SIGTERM) < 0 )
err(1, "kill: %" PRIdPID, init_pid);
return 0;
}

20
utils/reboot.8 Normal file
View File

@ -0,0 +1,20 @@
.Dd December 13, 2021
.Dt REBOOT 8
.Os
.Sh NAME
.Nm reboot
.Nd reboot the computer
.Sh SYNOPSIS
.Nm
.Sh DESCRIPTION
.Nm
gracefully reboots the computer by sending a request to the appropriate
.Xr init 8
process.
.Sh EXIT STATUS
.Nm
will exit 0 on success and non-zero otherwise.
.Sh SEE ALSO
.Xr halt 8 ,
.Xr init 8 ,
.Xr poweroff 8

50
utils/reboot.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* reboot.c
* Reboots the computer.
*/
#include <sys/types.h>
#include <err.h>
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int opt;
while ( (opt = getopt(argc, argv, "")) != -1 )
{
switch ( opt )
{
default: return 1;
}
}
if ( optind < argc )
errx(1, "extra operand: %s", argv[optind]);
pid_t init_pid = 1;
// TODO: Use a more reliable getinit() approach that also works in sshd.
if ( getenv("INIT_PID") )
init_pid = atoll(getenv("INIT_PID"));
if ( kill(init_pid, SIGINT) < 0 )
err(1, "kill: %" PRIdPID, init_pid);
return 0;
}