Handle SIGTERM in login(8).
Display a final frame with a message explaining what is happening that is displayed while the system powers off, reboots, or halts.
This commit is contained in:
parent
2bc6e40f1d
commit
65bc117891
3 changed files with 178 additions and 8 deletions
|
@ -63,6 +63,7 @@ enum stage
|
|||
STAGE_USERNAME,
|
||||
STAGE_PASSWORD,
|
||||
STAGE_CHECKING,
|
||||
STAGE_EXITING,
|
||||
};
|
||||
|
||||
struct textbox
|
||||
|
@ -111,6 +112,7 @@ struct glogin
|
|||
enum stage stage;
|
||||
bool animating;
|
||||
const char* warning;
|
||||
const char* announcement;
|
||||
bool pointer_working;
|
||||
struct termios old_tio;
|
||||
bool has_old_tio;
|
||||
|
@ -499,6 +501,33 @@ static void render_progress(struct framebuffer fb)
|
|||
}
|
||||
}
|
||||
|
||||
static void render_exit(struct framebuffer fb)
|
||||
{
|
||||
assert(state.announcement);
|
||||
|
||||
for ( int yoff = -1; yoff <= 1; yoff++ )
|
||||
{
|
||||
for ( int xoff = -1; xoff <= 1; xoff++ )
|
||||
{
|
||||
struct framebuffer msgfb = fb;
|
||||
int y = (fb.yres - FONT_HEIGHT) / 2 + yoff;
|
||||
msgfb = framebuffer_cut_top_y(msgfb, y);
|
||||
int w = strlen(state.announcement) * (FONT_WIDTH+1);
|
||||
int x = (fb.xres - w) / 2 + xoff;
|
||||
msgfb = framebuffer_cut_left_x(msgfb, x);
|
||||
render_text(msgfb, state.announcement, make_color_a(0, 0, 0, 64));
|
||||
}
|
||||
}
|
||||
|
||||
struct framebuffer msgfb = fb;
|
||||
int y = (fb.yres - FONT_HEIGHT) / 2;
|
||||
msgfb = framebuffer_cut_top_y(msgfb, y);
|
||||
int w = strlen(state.announcement) * (FONT_WIDTH+1);
|
||||
int x = (fb.xres - w) / 2;
|
||||
msgfb = framebuffer_cut_left_x(msgfb, x);
|
||||
render_text(msgfb, state.announcement, make_color(255, 255, 255));
|
||||
}
|
||||
|
||||
static void render_login(struct framebuffer fb)
|
||||
{
|
||||
render_background(fb);
|
||||
|
@ -509,6 +538,7 @@ static void render_login(struct framebuffer fb)
|
|||
case STAGE_USERNAME: render_form(fb); break;
|
||||
case STAGE_PASSWORD: render_form(fb); break;
|
||||
case STAGE_CHECKING: render_progress(fb); break;
|
||||
case STAGE_EXITING: render_exit(fb); break;
|
||||
}
|
||||
if ( state.pointer_working )
|
||||
render_pointer(fb);
|
||||
|
@ -669,6 +699,52 @@ static bool render(struct glogin* state)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void handle_special_graphical(struct glogin* state,
|
||||
enum special_action special_action)
|
||||
{
|
||||
switch ( special_action )
|
||||
{
|
||||
case SPECIAL_ACTION_NONE:
|
||||
state->announcement = NULL;
|
||||
break;
|
||||
case SPECIAL_ACTION_EXIT:
|
||||
state->announcement = "Exiting...";
|
||||
break;
|
||||
case SPECIAL_ACTION_POWEROFF:
|
||||
state->announcement = "Powering off...";
|
||||
break;
|
||||
case SPECIAL_ACTION_REBOOT:
|
||||
state->announcement = "Rebooting...";
|
||||
break;
|
||||
case SPECIAL_ACTION_HALT:
|
||||
state->announcement = "Halting...";
|
||||
break;
|
||||
case SPECIAL_ACTION_REINIT:
|
||||
state->announcement = "Reinitializing operating system...";
|
||||
break;
|
||||
}
|
||||
if ( state->announcement )
|
||||
{
|
||||
state->stage = STAGE_EXITING;
|
||||
state->fading_from = false;
|
||||
render(state);
|
||||
}
|
||||
handle_special(special_action);
|
||||
}
|
||||
|
||||
static int get_init_exit_plan(void)
|
||||
{
|
||||
FILE* fp = popen("/sbin/service default exit-code", "r");
|
||||
if ( !fp )
|
||||
return -1;
|
||||
int result = -1;
|
||||
char buffer[sizeof(int) * 3];
|
||||
if ( fgets(buffer, sizeof(buffer), fp) && buffer[0] )
|
||||
result = atoi(buffer);
|
||||
pclose(fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void think(struct glogin* state)
|
||||
{
|
||||
if ( state->stage == STAGE_CHECKING )
|
||||
|
@ -679,6 +755,7 @@ static void think(struct glogin* state)
|
|||
sched_yield();
|
||||
return;
|
||||
}
|
||||
forward_sigterm_to = 0;
|
||||
if ( result )
|
||||
{
|
||||
if ( !login(username, session) )
|
||||
|
@ -696,6 +773,21 @@ static void think(struct glogin* state)
|
|||
state->warning = strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
if ( got_sigterm )
|
||||
{
|
||||
int exit_code = get_init_exit_plan();
|
||||
enum special_action action = SPECIAL_ACTION_EXIT;
|
||||
if ( exit_code == 0 )
|
||||
action = SPECIAL_ACTION_POWEROFF;
|
||||
else if ( exit_code == 1 )
|
||||
action = SPECIAL_ACTION_REBOOT;
|
||||
else if ( exit_code == 2 )
|
||||
action = SPECIAL_ACTION_HALT;
|
||||
else if ( exit_code == 3 )
|
||||
action = SPECIAL_ACTION_REINIT;
|
||||
handle_special_graphical(state, action);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_event(struct glogin* state, uint32_t codepoint)
|
||||
|
@ -717,7 +809,7 @@ static void keyboard_event(struct glogin* state, uint32_t codepoint)
|
|||
state->warning = "Invalid username";
|
||||
break;
|
||||
}
|
||||
handle_special(action);
|
||||
handle_special_graphical(state, action);
|
||||
state->stage = STAGE_PASSWORD;
|
||||
textbox_reset(&textbox_password);
|
||||
break;
|
||||
|
@ -729,8 +821,10 @@ static void keyboard_event(struct glogin* state, uint32_t codepoint)
|
|||
state->stage = STAGE_USERNAME;
|
||||
state->warning = strerror(errno);
|
||||
}
|
||||
forward_sigterm_to = state->chk.pid;
|
||||
break;
|
||||
case STAGE_CHECKING:
|
||||
case STAGE_EXITING:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
@ -741,6 +835,7 @@ static void keyboard_event(struct glogin* state, uint32_t codepoint)
|
|||
case STAGE_USERNAME: textbox = &textbox_username; break;
|
||||
case STAGE_PASSWORD: textbox = &textbox_password; break;
|
||||
case STAGE_CHECKING: break;
|
||||
case STAGE_EXITING: break;
|
||||
}
|
||||
if ( textbox && codepoint < 128 )
|
||||
{
|
||||
|
@ -871,6 +966,12 @@ bool glogin_init(struct glogin* state)
|
|||
struct timespec duration = timespec_make(0, 150*1000*1000);
|
||||
state->fade_from_end = timespec_add(state->fade_from_begin, duration);
|
||||
}
|
||||
sigset_t sigterm;
|
||||
sigemptyset(&sigterm);
|
||||
sigaddset(&sigterm, SIGTERM);
|
||||
sigprocmask(SIG_BLOCK, &sigterm, NULL);
|
||||
struct sigaction sa = { .sa_handler = on_interrupt_signal };
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -900,9 +1001,14 @@ int glogin_main(struct glogin* state)
|
|||
nfds_t nfds = 2;
|
||||
struct timespec wake_now_ts = timespec_make(0, 0);
|
||||
struct timespec* wake = state->animating ? &wake_now_ts : NULL;
|
||||
int num_events = ppoll(pfds, nfds, wake, NULL);
|
||||
sigset_t pollmask;
|
||||
sigprocmask(SIG_SETMASK, NULL, &pollmask);
|
||||
sigdelset(&pollmask, SIGTERM);
|
||||
int num_events = ppoll(pfds, nfds, wake, &pollmask);
|
||||
if ( num_events < 0 )
|
||||
{
|
||||
if ( errno == EINTR )
|
||||
continue;
|
||||
warn("poll");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2015, 2018, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2014, 2015, 2018, 2022, 2023, 2024 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2023 dzwdz.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
|
@ -46,12 +46,21 @@
|
|||
|
||||
#include "login.h"
|
||||
|
||||
static void on_interrupt_signal(int signum)
|
||||
pid_t forward_sigterm_to = 0;
|
||||
volatile sig_atomic_t got_sigterm = 0;
|
||||
|
||||
void on_interrupt_signal(int signum)
|
||||
{
|
||||
if ( signum == SIGINT )
|
||||
dprintf(1, "^C");
|
||||
if ( signum == SIGQUIT )
|
||||
dprintf(1, "^\\");
|
||||
if ( signum == SIGTERM )
|
||||
{
|
||||
got_sigterm = 1;
|
||||
if ( forward_sigterm_to )
|
||||
kill(forward_sigterm_to, SIGTERM);
|
||||
}
|
||||
}
|
||||
|
||||
bool check_real(const char* username, const char* password)
|
||||
|
@ -112,8 +121,10 @@ bool check_begin(struct check* chk,
|
|||
{
|
||||
sigdelset(&chk->oldset, SIGINT);
|
||||
sigdelset(&chk->oldset, SIGQUIT);
|
||||
sigdelset(&chk->oldset, SIGTERM);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
unsigned int termmode = TERMMODE_UNICODE | TERMMODE_SIGNAL |
|
||||
TERMMODE_UTF8 | TERMMODE_LINEBUFFER |
|
||||
TERMMODE_ECHO;
|
||||
|
@ -144,6 +155,17 @@ bool check_end(struct check* chk, bool* result, bool try)
|
|||
fcntl(chk->pipe, F_SETFL, fcntl(chk->pipe, F_GETFL) | O_NONBLOCK);
|
||||
chk->pipe_nonblock = true;
|
||||
}
|
||||
sigset_t sigterm, oldset;
|
||||
sigemptyset(&sigterm);
|
||||
sigaddset(&sigterm, SIGTERM);
|
||||
struct sigaction sa = { .sa_handler = on_interrupt_signal }, old_sa;
|
||||
if ( !try )
|
||||
{
|
||||
sigprocmask(SIG_BLOCK, &sigterm, &oldset);
|
||||
forward_sigterm_to = chk->pid;
|
||||
sigaction(SIGTERM, &sa, &old_sa);
|
||||
sigprocmask(SIG_UNBLOCK, &sigterm, NULL);
|
||||
}
|
||||
while ( chk->errnum_done < sizeof(chk->errnum_bytes) )
|
||||
{
|
||||
ssize_t amount = read(chk->pipe, chk->errnum_bytes + chk->errnum_done,
|
||||
|
@ -156,6 +178,13 @@ bool check_end(struct check* chk, bool* result, bool try)
|
|||
}
|
||||
chk->errnum_done += amount;
|
||||
}
|
||||
if ( !try )
|
||||
{
|
||||
sigprocmask(SIG_BLOCK, &sigterm, NULL);
|
||||
forward_sigterm_to = 0;
|
||||
sigaction(SIGTERM, &old_sa, NULL);
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
}
|
||||
int code;
|
||||
pid_t wait_ret = waitpid(chk->pid, &code, try ? WNOHANG : 0);
|
||||
if ( try && wait_ret == 0 )
|
||||
|
@ -230,11 +259,17 @@ bool login(const char* username, const char* session)
|
|||
return free(login_shell), close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
if ( child_pid == 0 )
|
||||
{
|
||||
sigset_t sigterm;
|
||||
sigemptyset(&sigterm);
|
||||
sigaddset(&sigterm, SIGTERM);
|
||||
sigprocmask(SIG_UNBLOCK, &sigterm, NULL);
|
||||
sigdelset(&oldset, SIGINT);
|
||||
sigdelset(&oldset, SIGQUIT);
|
||||
sigdelset(&oldset, SIGTERM);
|
||||
sigdelset(&oldset, SIGTSTP);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
(void) (
|
||||
setpgid(0, 0) < 0 ||
|
||||
close(pipe_fds[0]) < 0 ||
|
||||
|
@ -269,13 +304,33 @@ bool login(const char* username, const char* session)
|
|||
}
|
||||
free(login_shell);
|
||||
close(pipe_fds[1]);
|
||||
|
||||
sigset_t sigterm;
|
||||
sigemptyset(&sigterm);
|
||||
sigaddset(&sigterm, SIGTERM);
|
||||
sigprocmask(SIG_BLOCK, &sigterm, NULL);
|
||||
struct sigaction sa = { .sa_handler = on_interrupt_signal }, old_sa;
|
||||
forward_sigterm_to = child_pid;
|
||||
sigaction(SIGTERM, &sa, &old_sa);
|
||||
sigprocmask(SIG_UNBLOCK, &sigterm, NULL);
|
||||
|
||||
int errnum;
|
||||
if ( readall(pipe_fds[0], &errnum, sizeof(errnum)) < (ssize_t) sizeof(errnum) )
|
||||
errnum = 0;
|
||||
close(pipe_fds[0]);
|
||||
int child_status;
|
||||
if ( waitpid(child_pid, &child_status, 0) < 0 )
|
||||
while ( waitpid(child_pid, &child_status, 0) < 0 )
|
||||
{
|
||||
if ( errno == EINTR )
|
||||
continue;
|
||||
errnum = errno;
|
||||
break;
|
||||
}
|
||||
|
||||
sigprocmask(SIG_BLOCK, &sigterm, NULL);
|
||||
forward_sigterm_to = 0;
|
||||
sigaction(SIGTERM, &old_sa, NULL);
|
||||
|
||||
tcsetattr(0, TCSAFLUSH, &tio);
|
||||
tcsetpgrp(0, getpgid(0));
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
|
@ -334,7 +389,7 @@ bool parse_username(const char* input,
|
|||
*session = NULL;
|
||||
*action = SPECIAL_ACTION_NONE;
|
||||
if ( !strcmp(input, "exit") )
|
||||
return *action = SPECIAL_ACTION_POWEROFF, true;
|
||||
return *action = SPECIAL_ACTION_EXIT, true;
|
||||
else if ( !strcmp(input, "poweroff") )
|
||||
return *action = SPECIAL_ACTION_POWEROFF, true;
|
||||
else if ( !strcmp(input, "reboot") )
|
||||
|
@ -376,6 +431,7 @@ void handle_special(enum special_action action)
|
|||
switch ( action )
|
||||
{
|
||||
case SPECIAL_ACTION_NONE: return;
|
||||
case SPECIAL_ACTION_EXIT: exit(0);
|
||||
case SPECIAL_ACTION_POWEROFF: exit(0);
|
||||
case SPECIAL_ACTION_REBOOT: exit(1);
|
||||
case SPECIAL_ACTION_HALT: exit(2);
|
||||
|
@ -394,7 +450,7 @@ int textual(void)
|
|||
char* username = NULL;
|
||||
char* session = NULL;
|
||||
|
||||
while ( true )
|
||||
while ( !got_sigterm )
|
||||
{
|
||||
char hostname[HOST_NAME_MAX + 1];
|
||||
hostname[0] = '\0';
|
||||
|
@ -460,6 +516,9 @@ int textual(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
if ( got_sigterm )
|
||||
break;
|
||||
|
||||
if ( !login(username, session) )
|
||||
{
|
||||
warn("logging in as %s", username);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2015, 2023 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2014, 2015, 2023, 2024 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2023 dzwdz.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
|
@ -39,12 +39,14 @@ struct check
|
|||
enum special_action
|
||||
{
|
||||
SPECIAL_ACTION_NONE,
|
||||
SPECIAL_ACTION_EXIT,
|
||||
SPECIAL_ACTION_POWEROFF,
|
||||
SPECIAL_ACTION_REBOOT,
|
||||
SPECIAL_ACTION_HALT,
|
||||
SPECIAL_ACTION_REINIT,
|
||||
};
|
||||
|
||||
void on_interrupt_signal(int signum);
|
||||
bool login(const char* username, const char* session);
|
||||
bool check_real(const char* username, const char* password);
|
||||
bool check_begin(struct check* chk,
|
||||
|
@ -62,4 +64,7 @@ bool parse_username(const char* input,
|
|||
enum special_action* action);
|
||||
void handle_special(enum special_action action);
|
||||
|
||||
extern pid_t forward_sigterm_to;
|
||||
extern volatile sig_atomic_t got_sigterm;
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue