You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-plymouth/src/main.c

2199 lines
59 KiB

/* main.c - boot messages monitor
*
* Copyright (C) 2007 Red Hat, Inc
*
* This file 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 file 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; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Written by: Ray Strode <rstrode@redhat.com>
*/
#include "config.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <limits.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sysexits.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <wchar.h>
#include <paths.h>
#include <assert.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include "ply-buffer.h"
#include "ply-command-parser.h"
#include "ply-boot-server.h"
#include "ply-boot-splash.h"
#include "ply-event-loop.h"
#include "ply-hashtable.h"
#include "ply-list.h"
#include "ply-logger.h"
#include "ply-terminal-session.h"
#include "ply-trigger.h"
#include "ply-utils.h"
#include "ply-progress.h"
#ifndef PLY_MAX_COMMAND_LINE_SIZE
#define PLY_MAX_COMMAND_LINE_SIZE 512
#endif
#define BOOT_DURATION_FILE PLYMOUTH_TIME_DIRECTORY "/boot-duration"
#define SHUTDOWN_DURATION_FILE PLYMOUTH_TIME_DIRECTORY "/shutdown-duration"
typedef enum {
PLY_MODE_BOOT,
PLY_MODE_SHUTDOWN
} ply_mode_t;
typedef struct
{
const char *keys;
ply_trigger_t *trigger;
} ply_keystroke_watch_t;
typedef struct
{
enum {PLY_ENTRY_TRIGGER_TYPE_PASSWORD,
PLY_ENTRY_TRIGGER_TYPE_QUESTION}
type;
const char *prompt;
ply_trigger_t *trigger;
} ply_entry_trigger_t;
typedef struct
{
ply_event_loop_t *loop;
ply_boot_server_t *boot_server;
ply_list_t *pixel_displays;
ply_list_t *text_displays;
ply_keyboard_t *keyboard;
ply_boot_splash_t *boot_splash;
ply_terminal_session_t *session;
ply_buffer_t *boot_buffer;
ply_progress_t *progress;
ply_list_t *keystroke_triggers;
ply_list_t *entry_triggers;
ply_buffer_t *entry_buffer;
ply_list_t *messages;
ply_command_parser_t *command_parser;
ply_mode_t mode;
ply_renderer_t *renderer;
ply_terminal_t *terminal;
ply_trigger_t *deactivate_trigger;
ply_trigger_t *quit_trigger;
char kernel_command_line[PLY_MAX_COMMAND_LINE_SIZE];
uint32_t no_boot_log : 1;
uint32_t showing_details : 1;
uint32_t system_initialized : 1;
uint32_t is_redirected : 1;
uint32_t is_attached : 1;
uint32_t should_be_attached : 1;
uint32_t should_retain_splash : 1;
uint32_t is_inactive : 1;
uint32_t should_force_details : 1;
char *kernel_console_tty;
char *override_splash_path;
char *system_default_splash_path;
char *distribution_default_splash_path;
const char *default_tty;
int number_of_errors;
} state_t;
static ply_boot_splash_t *start_boot_splash (state_t *state,
const char *theme_path,
bool fall_back_if_neccessary);
static void add_display_and_keyboard_for_terminal (state_t *state,
const char *tty_name);
static void add_default_displays_and_keyboard (state_t *state);
static bool attach_to_running_session (state_t *state);
static void on_escape_pressed (state_t *state);
static void dump_details_and_quit_splash (state_t *state);
static void update_display (state_t *state);
static void on_error_message (ply_buffer_t *debug_buffer,
const void *bytes,
size_t number_of_bytes);
static ply_buffer_t *debug_buffer;
static char *debug_buffer_path = NULL;
static char *pid_file = NULL;
static void check_for_consoles (state_t *state,
const char *default_tty,
bool should_add_displays);
static void toggle_between_splash_and_details (state_t *state);
static void
on_session_output (state_t *state,
const char *output,
size_t size)
{
ply_buffer_append_bytes (state->boot_buffer, output, size);
if (state->boot_splash != NULL)
ply_boot_splash_update_output (state->boot_splash,
output, size);
}
static void
on_session_hangup (state_t *state)
{
ply_trace ("got hang up on terminal session fd");
}
static void
on_update (state_t *state,
const char *status)
{
ply_trace ("updating status to '%s'", status);
ply_progress_status_update (state->progress,
status);
if (state->boot_splash != NULL)
ply_boot_splash_update_status (state->boot_splash,
status);
}
static void
show_messages (state_t *state)
{
if (state->boot_splash == NULL)
{
ply_trace ("not displaying messages, since no boot splash");
return;
}
ply_list_node_t *node = ply_list_get_first_node (state->messages);
while (node != NULL)
{
ply_list_node_t *next_node;
char *message = ply_list_node_get_data (node);
ply_trace ("displaying messages");
ply_boot_splash_display_message (state->boot_splash, message);
next_node = ply_list_get_next_node (state->messages, node);
node = next_node;
}
}
static void
show_detailed_splash (state_t *state)
{
if (state->boot_splash != NULL)
return;
ply_trace ("Showing detailed splash screen");
state->boot_splash = start_boot_splash (state,
PLYMOUTH_THEME_PATH "details/details.plymouth",
true);
if (state->boot_splash == NULL)
{
ply_trace ("Could not start detailed splash screen, this could be a problem.");
}
}
static const char *
command_line_get_string_after_prefix (const char *command_line,
const char *prefix)
{
char *argument;
argument = strstr (command_line, prefix);
if (argument == NULL)
return NULL;
if (argument == command_line ||
argument[-1] == ' ')
return argument + strlen (prefix);
return NULL;
}
static bool
command_line_has_argument (const char *command_line,
const char *argument)
{
const char *string;
string = command_line_get_string_after_prefix (command_line, argument);
if (string == NULL)
return false;
if (!isspace ((int) string[0]) && string[0] != '\0')
return false;
return true;
}
static void
find_override_splash (state_t *state)
{
const char *splash_string;
if (state->override_splash_path != NULL)
return;
splash_string = command_line_get_string_after_prefix (state->kernel_command_line,
"plymouth.splash=");
if (splash_string != NULL)
{
const char *end;
int length;
end = splash_string + strcspn (splash_string, " \n");
length = end - splash_string;
ply_trace ("Splash is configured to be '%*.*s'", length, length, splash_string);
asprintf (&state->override_splash_path,
PLYMOUTH_THEME_PATH "%*.*s/%*.*s.plymouth",
length, length, splash_string, length, length, splash_string);
}
}
static void
find_system_default_splash (state_t *state)
{
ply_key_file_t *key_file;
char *splash_string;
if (state->system_default_splash_path != NULL)
return;
ply_trace ("Trying to load " PLYMOUTH_CONF_DIR "plymouthd.conf");
key_file = ply_key_file_new (PLYMOUTH_CONF_DIR "plymouthd.conf");
if (!ply_key_file_load (key_file))
{
ply_trace ("failed to load " PLYMOUTH_CONF_DIR "plymouthd.conf");
ply_key_file_free (key_file);
return;
}
splash_string = ply_key_file_get_value (key_file, "Daemon", "Theme");
ply_trace ("System default splash is configured to be '%s'", splash_string);
asprintf (&state->system_default_splash_path,
PLYMOUTH_THEME_PATH "%s/%s.plymouth",
splash_string, splash_string);
free (splash_string);
}
static void
find_distribution_default_splash (state_t *state)
{
ply_key_file_t *key_file;
char *splash_string;
if (state->distribution_default_splash_path != NULL)
return;
ply_trace ("Trying to load " PLYMOUTH_POLICY_DIR "plymouthd.defaults");
key_file = ply_key_file_new (PLYMOUTH_POLICY_DIR "plymouthd.defaults");
if (!ply_key_file_load (key_file))
{
ply_trace ("failed to load " PLYMOUTH_POLICY_DIR "plymouthd.defaults");
ply_key_file_free (key_file);
return;
}
splash_string = ply_key_file_get_value (key_file, "Daemon", "Theme");
ply_trace ("Distribution default splash is configured to be '%s'", splash_string);
asprintf (&state->distribution_default_splash_path,
PLYMOUTH_THEME_PATH "%s/%s.plymouth",
splash_string, splash_string);
free (splash_string);
}
static void
show_default_splash (state_t *state)
{
if (state->boot_splash != NULL)
return;
ply_trace ("Showing splash screen");
find_override_splash (state);
if (state->override_splash_path != NULL)
{
ply_trace ("Trying override splash at '%s'", state->override_splash_path);
state->boot_splash = start_boot_splash (state,
state->override_splash_path,
false);
}
find_system_default_splash (state);
if (state->boot_splash == NULL &&
state->system_default_splash_path != NULL)
{
ply_trace ("Trying system default splash");
state->boot_splash = start_boot_splash (state,
state->system_default_splash_path,
false);
}
find_distribution_default_splash (state);
if (state->boot_splash == NULL &&
state->distribution_default_splash_path != NULL)
{
ply_trace ("Trying distribution default splash");
state->boot_splash = start_boot_splash (state,
state->distribution_default_splash_path,
false);
}
if (state->boot_splash == NULL)
{
ply_trace ("Trying old scheme for default splash");
state->boot_splash = start_boot_splash (state,
PLYMOUTH_THEME_PATH "default.plymouth",
false);
}
if (state->boot_splash == NULL)
{
ply_trace ("Could not start default splash screen,"
"showing text splash screen");
state->boot_splash = start_boot_splash (state,
PLYMOUTH_THEME_PATH "text/text.plymouth",
false);
}
if (state->boot_splash == NULL)
{
ply_trace ("Could not start text splash screen,"
"showing built-in fallback");
state->boot_splash = start_boot_splash (state,
PLYMOUTH_THEME_PATH "text/text.plymouth",
true);
}
if (state->boot_splash == NULL)
ply_error ("plymouthd: could not start boot splash: %m");
}
static void
on_ask_for_password (state_t *state,
const char *prompt,
ply_trigger_t *answer)
{
ply_entry_trigger_t *entry_trigger;
/* No splash, client will have to get password
*/
if (state->boot_splash == NULL)
{
ply_trace ("no splash loaded, replying immediately with no password");
ply_trigger_pull (answer, NULL);
return;
}
entry_trigger = calloc (1, sizeof (ply_entry_trigger_t));
entry_trigger->type = PLY_ENTRY_TRIGGER_TYPE_PASSWORD;
entry_trigger->prompt = prompt;
entry_trigger->trigger = answer;
ply_trace ("queuing password request with boot splash");
ply_list_append_data (state->entry_triggers, entry_trigger);
update_display (state);
}
static void
on_ask_question (state_t *state,
const char *prompt,
ply_trigger_t *answer)
{
ply_entry_trigger_t *entry_trigger;
entry_trigger = calloc (1, sizeof (ply_entry_trigger_t));
entry_trigger->type = PLY_ENTRY_TRIGGER_TYPE_QUESTION;
entry_trigger->prompt = prompt;
entry_trigger->trigger = answer;
ply_trace ("queuing question with boot splash");
ply_list_append_data (state->entry_triggers, entry_trigger);
update_display (state);
}
static void
on_display_message (state_t *state,
const char *message)
{
ply_trace ("displaying message %s", message);
if (state->boot_splash != NULL)
ply_boot_splash_display_message (state->boot_splash, message);
ply_list_append_data (state->messages, strdup(message));
}
static void
on_hide_message (state_t *state,
const char *message)
{
ply_list_node_t *node;
ply_trace ("hiding message %s", message);
node = ply_list_get_first_node (state->messages);
while (node != NULL)
{
ply_list_node_t *next_node;
char *list_message;
list_message = ply_list_node_get_data (node);
next_node = ply_list_get_next_node (state->messages, node);
if (strcmp (list_message, message) == 0)
{
free (list_message);
ply_list_remove_node (state->messages, node);
ply_boot_splash_hide_message (state->boot_splash, message);
}
node = next_node;
}
}
static void
on_watch_for_keystroke (state_t *state,
const char *keys,
ply_trigger_t *trigger)
{
ply_keystroke_watch_t *keystroke_trigger =
calloc (1, sizeof (ply_keystroke_watch_t));
ply_trace ("watching for keystroke");
keystroke_trigger->keys = keys;
keystroke_trigger->trigger = trigger;
ply_list_append_data (state->keystroke_triggers, keystroke_trigger);
}
static void
on_ignore_keystroke (state_t *state,
const char *keys)
{
ply_list_node_t *node;
ply_trace ("ignoring for keystroke");
for (node = ply_list_get_first_node (state->keystroke_triggers); node;
node = ply_list_get_next_node (state->keystroke_triggers, node))
{
ply_keystroke_watch_t* keystroke_trigger = ply_list_node_get_data (node);
if ((!keystroke_trigger->keys && !keys) ||
(keystroke_trigger->keys && keys && strcmp(keystroke_trigger->keys, keys)==0))
{
ply_trigger_pull (keystroke_trigger->trigger, NULL);
ply_list_remove_node (state->keystroke_triggers, node);
return;
}
}
}
static void
on_progress_pause (state_t *state)
{
ply_trace ("pausing progress");
ply_progress_pause (state->progress);
}
static void
on_progress_unpause (state_t *state)
{
ply_trace ("unpausing progress");
ply_progress_unpause (state->progress);
}
static void
on_newroot (state_t *state,
const char *root_dir)
{
if (state->mode != PLY_MODE_BOOT)
{
ply_trace ("new root is only supported in boot mode ");
return;
}
ply_trace ("new root mounted at \"%s\", switching to it", root_dir);
chdir(root_dir);
chroot(".");
chdir("/");
ply_progress_load_cache (state->progress, BOOT_DURATION_FILE);
if (state->boot_splash != NULL)
ply_boot_splash_root_mounted (state->boot_splash);
}
static const char *
get_cache_file_for_mode (ply_mode_t mode)
{
const char *filename;
switch ((int)mode)
{
case PLY_MODE_BOOT:
filename = BOOT_DURATION_FILE;
break;
case PLY_MODE_SHUTDOWN:
filename = SHUTDOWN_DURATION_FILE;
break;
default:
fprintf (stderr, "Unhandled case in %s line %d\n", __FILE__, __LINE__);
abort ();
break;
}
ply_trace ("returning cache file '%s'", filename);
return filename;
}
static const char *
get_log_file_for_mode (ply_mode_t mode)
{
const char *filename;
switch ((int)mode)
{
case PLY_MODE_BOOT:
filename = PLYMOUTH_LOG_DIRECTORY "/boot.log";
break;
case PLY_MODE_SHUTDOWN:
filename = _PATH_DEVNULL;
break;
default:
fprintf (stderr, "Unhandled case in %s line %d\n", __FILE__, __LINE__);
abort ();
break;
}
ply_trace ("returning log file '%s'", filename);
return filename;
}
static const char *
get_log_spool_file_for_mode (ply_mode_t mode)
{
const char *filename;
switch ((int)mode)
{
case PLY_MODE_BOOT:
filename = PLYMOUTH_SPOOL_DIRECTORY "/boot.log";
break;
case PLY_MODE_SHUTDOWN:
filename = NULL;
break;
default:
fprintf (stderr, "Unhandled case in %s line %d\n", __FILE__, __LINE__);
abort ();
break;
}
ply_trace ("returning spool file '%s'", filename);
return filename;
}
static void
spool_error (state_t *state)
{
const char *logfile;
const char *logspool;
ply_trace ("spooling error for viewer");
logfile = get_log_file_for_mode (state->mode);
logspool = get_log_spool_file_for_mode (state->mode);
if (logfile != NULL && logspool != NULL)
{
unlink (logspool);
ply_create_file_link (logfile, logspool);
}
}
static void
prepare_logging (state_t *state)
{
const char *logfile;
if (!state->system_initialized)
{
ply_trace ("not preparing logging yet, system not initialized");
return;
}
if (state->session == NULL)
{
ply_trace ("not preparing logging, no session");
return;
}
logfile = get_log_file_for_mode (state->mode);
if (logfile != NULL)
{
ply_trace ("opening log '%s'", logfile);
ply_terminal_session_open_log (state->session, logfile);
if (state->number_of_errors > 0)
spool_error (state);
}
}
static void
on_system_initialized (state_t *state)
{
ply_trace ("system now initialized, opening log");
state->system_initialized = true;
prepare_logging (state);
}
static void
on_error (state_t *state)
{
ply_trace ("encountered error during boot up");
if (state->system_initialized && state->number_of_errors == 0)
spool_error (state);
else
ply_trace ("not spooling because number of errors %d", state->number_of_errors);
state->number_of_errors++;
}
static bool
plymouth_should_ignore_show_splash_calls (state_t *state)
{
const char *init_string;
size_t length;
ply_trace ("checking if plymouth should be running");
if (state->mode != PLY_MODE_BOOT || command_line_has_argument (state->kernel_command_line, "plymouth.force-splash"))
return false;
if (command_line_has_argument (state->kernel_command_line, "plymouth.ignore-show-splash"))
return true;
init_string = command_line_get_string_after_prefix (state->kernel_command_line, "init=");
if (init_string)
{
length = strcspn (init_string, " \n");
if (length > 2 && ply_string_has_prefix (init_string + length - 2, "sh"))
return true;
}
return false;
}
static bool
plymouth_should_show_default_splash (state_t *state)
{
ply_trace ("checking if plymouth should show default splash");
const char const *strings[] = {
"single", "1", "s", "S", "-S", NULL
};
int i;
if (state->should_force_details)
return false;
for (i = 0; strings[i] != NULL; i++)
{
if (command_line_has_argument (state->kernel_command_line, strings[i]))
{
ply_trace ("no default splash because kernel command line has option \"%s\"", strings[i]);
return false;
}
}
if (command_line_has_argument (state->kernel_command_line, "splash=verbose"))
{
ply_trace ("no default splash because kernel command line has option \"splash=verbose\"");
return false;
}
if (command_line_has_argument (state->kernel_command_line, "rhgb"))
{
ply_trace ("using default splash because kernel command line has option \"rhgb\"");
return true;
}
if (command_line_has_argument (state->kernel_command_line, "splash"))
{
ply_trace ("using default splash because kernel command line has option \"splash\"");
return true;
}
ply_trace ("no default splash because kernel command line lacks \"splash\" or \"rhgb\"");
return false;
}
static void
remove_displays_and_keyboard (state_t *state)
{
ply_list_node_t *node;
ply_trace ("removing displays and keyboard");
node = ply_list_get_first_node (state->pixel_displays);
while (node != NULL)
{
ply_list_node_t *next_node;
ply_pixel_display_t *display;
ply_trace ("removing pixel display");
next_node = ply_list_get_next_node (state->pixel_displays, node);
display = ply_list_node_get_data (node);
ply_pixel_display_free (display);
ply_list_remove_node (state->pixel_displays, node);
node = next_node;
}
node = ply_list_get_first_node (state->text_displays);
while (node != NULL)
{
ply_list_node_t *next_node;
ply_text_display_t *display;
ply_trace ("removing text display");
next_node = ply_list_get_next_node (state->text_displays, node);
display = ply_list_node_get_data (node);
ply_text_display_free (display);
ply_list_remove_node (state->text_displays, node);
node = next_node;
}
if (state->keyboard != NULL)
{
ply_trace ("removing keyboard");
ply_keyboard_free (state->keyboard);
state->keyboard = NULL;
}
}
static void
on_show_splash (state_t *state)
{
bool has_display;
if (state->is_inactive)
{
ply_trace ("show splash called while inactive");
return;
}
if (plymouth_should_ignore_show_splash_calls (state))
{
ply_trace ("show splash called while ignoring show splash calls");
dump_details_and_quit_splash (state);
return;
}
check_for_consoles (state, state->default_tty, true);
has_display = ply_list_get_length (state->pixel_displays) > 0 ||
ply_list_get_length (state->text_displays) > 0;
if (!state->is_attached && state->should_be_attached && has_display)
attach_to_running_session (state);
if (!has_display && state->is_attached)
{
ply_trace ("no open seats, detaching session");
ply_terminal_session_detach (state->session);
state->is_redirected = false;
state->is_attached = false;
}
if (plymouth_should_show_default_splash (state))
{
show_default_splash (state);
state->showing_details = false;
}
else
{
show_detailed_splash (state);
state->showing_details = true;
}
show_messages (state);
}
static void
quit_splash (state_t *state)
{
ply_trace ("quiting splash");
if (state->boot_splash != NULL)
{
ply_trace ("freeing splash");
ply_boot_splash_free (state->boot_splash);
state->boot_splash = NULL;
}
ply_trace ("removing displays and keyboard");
remove_displays_and_keyboard (state);
if (state->renderer != NULL)
{
ply_renderer_close (state->renderer);
ply_renderer_free (state->renderer);
state->renderer = NULL;
}
if (state->terminal != NULL)
{
if (!state->should_retain_splash)
{
ply_trace ("Not retaining splash, so deallocating VT");
ply_terminal_deactivate_vt (state->terminal);
}
ply_terminal_close (state->terminal);
ply_terminal_free (state->terminal);
state->terminal = NULL;
}
if (state->session != NULL)
{
ply_trace ("detaching session");
ply_terminal_session_detach (state->session);
state->is_redirected = false;
state->is_attached = false;
}
}
static void
dump_details_and_quit_splash (state_t *state)
{
state->showing_details = false;
toggle_between_splash_and_details (state);
if (state->renderer != NULL)
ply_renderer_deactivate (state->renderer);
if (state->boot_splash != NULL)
ply_boot_splash_hide (state->boot_splash);
quit_splash (state);
}
static void
on_hide_splash (state_t *state)
{
if (state->is_inactive)
return;
if (state->boot_splash == NULL)
return;
ply_trace ("hiding boot splash");
dump_details_and_quit_splash (state);
}
#ifdef PLY_ENABLE_GDM_TRANSITION
static void
tell_gdm_to_transition (void)
{
int fd;
fd = creat ("/var/spool/gdm/force-display-on-active-vt", 0644);
close (fd);
}
#endif
static void
quit_program (state_t *state)
{
ply_trace ("exiting event loop");
ply_event_loop_exit (state->loop, 0);
if (pid_file != NULL)
{
unlink (pid_file);
free (pid_file);
pid_file = NULL;
}
#ifdef PLY_ENABLE_GDM_TRANSITION
if (state->should_retain_splash)
{
tell_gdm_to_transition ();
}
#endif
if (state->deactivate_trigger != NULL)
{
ply_trigger_pull (state->deactivate_trigger, NULL);
state->deactivate_trigger = NULL;
}
if (state->quit_trigger != NULL)
{
ply_trigger_pull (state->quit_trigger, NULL);
state->quit_trigger = NULL;
}
}
static void
deactivate_splash (state_t *state)
{
assert (!state->is_inactive);
if (state->renderer != NULL)
{
ply_trace ("deactivating renderer");
ply_renderer_deactivate (state->renderer);
}
if ((state->session != NULL) && state->is_attached)
{
ply_trace ("deactivating terminal session");
ply_terminal_session_detach (state->session);
state->is_redirected = false;
state->is_attached = false;
}
if (state->terminal != NULL)
{
ply_trace ("deactivating terminal");
ply_terminal_stop_watching_for_vt_changes (state->terminal);
ply_terminal_set_buffered_input (state->terminal);
ply_terminal_ignore_mode_changes (state->terminal, true);
}
state->is_inactive = true;
ply_trigger_pull (state->deactivate_trigger, NULL);
state->deactivate_trigger = NULL;
}
static void
on_boot_splash_idle (state_t *state)
{
ply_trace ("boot splash idle");
/* In the case where we've received both a deactivate command and a
* quit command, the quit command takes precedence.
*/
if (state->quit_trigger != NULL)
{
if (!state->should_retain_splash)
{
ply_trace ("hiding splash");
if (state->renderer != NULL)
ply_renderer_deactivate (state->renderer);
if (state->boot_splash != NULL)
ply_boot_splash_hide (state->boot_splash);
}
ply_trace ("quitting splash");
quit_splash (state);
ply_trace ("quitting program");
quit_program (state);
}
else if (state->deactivate_trigger != NULL)
{
ply_trace ("deactivating splash");
deactivate_splash (state);
}
}
static void
on_deactivate (state_t *state,
ply_trigger_t *deactivate_trigger)
{
if ((state->deactivate_trigger != NULL) || state->is_inactive)
{
ply_trigger_add_handler (state->deactivate_trigger,
(ply_trigger_handler_t)
ply_trigger_pull,
deactivate_trigger);
return;
}
state->deactivate_trigger = deactivate_trigger;
ply_trace ("deactivating");
if (state->keyboard != NULL)
{
ply_trace ("deactivating keyboard");
ply_keyboard_stop_watching_for_input (state->keyboard);
}
if (state->boot_splash != NULL)
{
ply_boot_splash_become_idle (state->boot_splash,
(ply_boot_splash_on_idle_handler_t)
on_boot_splash_idle,
state);
}
else
{
ply_trace ("deactivating splash");
deactivate_splash (state);
}
}
static void
on_reactivate (state_t *state)
{
if (!state->is_inactive)
return;
if (state->terminal != NULL)
{
ply_terminal_watch_for_vt_changes (state->terminal);
ply_terminal_set_unbuffered_input (state->terminal);
ply_terminal_ignore_mode_changes (state->terminal, false);
}
if ((state->session != NULL) && state->should_be_attached)
{
ply_trace ("reactivating terminal session");
attach_to_running_session (state);
}
if (state->keyboard != NULL)
{
ply_trace ("activating keyboard");
ply_keyboard_watch_for_input (state->keyboard);
}
if (state->renderer != NULL)
{
ply_trace ("activating renderer");
ply_renderer_activate (state->renderer);
}
state->is_inactive = false;
update_display (state);
}
static void
on_quit (state_t *state,
bool retain_splash,
ply_trigger_t *quit_trigger)
{
if (state->quit_trigger != NULL)
{
ply_trigger_add_handler (state->quit_trigger,
(ply_trigger_handler_t)
ply_trigger_pull,
quit_trigger);
return;
}
if (state->system_initialized)
ply_progress_save_cache (state->progress,
get_cache_file_for_mode (state->mode));
state->quit_trigger = quit_trigger;
state->should_retain_splash = retain_splash;
ply_trace ("time to quit, closing log");
if (state->session != NULL)
ply_terminal_session_close_log (state->session);
if (state->keyboard != NULL)
{
ply_trace ("deactivating keyboard");
ply_keyboard_stop_watching_for_input (state->keyboard);
}
ply_trace ("unloading splash");
if (state->is_inactive && !retain_splash)
{
/* We've been deactivated and X failed to start
*/
dump_details_and_quit_splash (state);
quit_program (state);
}
else if (state->boot_splash != NULL)
{
ply_boot_splash_become_idle (state->boot_splash,
(ply_boot_splash_on_idle_handler_t)
on_boot_splash_idle,
state);
}
else
quit_program (state);
}
static bool
on_has_active_vt (state_t *state)
{
if (state->terminal != NULL)
return ply_terminal_is_active (state->terminal);
else
return false;
}
static ply_boot_server_t *
start_boot_server (state_t *state)
{
ply_boot_server_t *server;
server = ply_boot_server_new ((ply_boot_server_update_handler_t) on_update,
(ply_boot_server_ask_for_password_handler_t) on_ask_for_password,
(ply_boot_server_ask_question_handler_t) on_ask_question,
(ply_boot_server_display_message_handler_t) on_display_message,
(ply_boot_server_hide_message_handler_t) on_hide_message,
(ply_boot_server_watch_for_keystroke_handler_t) on_watch_for_keystroke,
(ply_boot_server_ignore_keystroke_handler_t) on_ignore_keystroke,
(ply_boot_server_progress_pause_handler_t) on_progress_pause,
(ply_boot_server_progress_unpause_handler_t) on_progress_unpause,
(ply_boot_server_show_splash_handler_t) on_show_splash,
(ply_boot_server_hide_splash_handler_t) on_hide_splash,
(ply_boot_server_newroot_handler_t) on_newroot,
(ply_boot_server_system_initialized_handler_t) on_system_initialized,
(ply_boot_server_error_handler_t) on_error,
(ply_boot_server_deactivate_handler_t) on_deactivate,
(ply_boot_server_reactivate_handler_t) on_reactivate,
(ply_boot_server_quit_handler_t) on_quit,
(ply_boot_server_has_active_vt_handler_t) on_has_active_vt,
state);
if (!ply_boot_server_listen (server))
{
ply_save_errno ();
ply_boot_server_free (server);
ply_restore_errno ();
return NULL;
}
ply_boot_server_attach_to_event_loop (server, state->loop);
return server;
}
static void
update_display (state_t *state)
{
if (!state->boot_splash) return;
ply_list_node_t *node;
node = ply_list_get_first_node (state->entry_triggers);
if (node)
{
ply_entry_trigger_t* entry_trigger = ply_list_node_get_data (node);
if (entry_trigger->type == PLY_ENTRY_TRIGGER_TYPE_PASSWORD)
{
int bullets = ply_utf8_string_get_length (ply_buffer_get_bytes (state->entry_buffer),
ply_buffer_get_size (state->entry_buffer));
bullets = MAX(0, bullets);
ply_boot_splash_display_password (state->boot_splash,
entry_trigger->prompt,
bullets);
}
else if (entry_trigger->type == PLY_ENTRY_TRIGGER_TYPE_QUESTION)
{
ply_boot_splash_display_question (state->boot_splash,
entry_trigger->prompt,
ply_buffer_get_bytes (state->entry_buffer));
}
else {
ply_trace("unkown entry type");
}
}
else
{
ply_boot_splash_display_normal (state->boot_splash);
}
}
static void
toggle_between_splash_and_details (state_t *state)
{
ply_trace ("toggling between splash and details");
if (state->boot_splash != NULL)
{
ply_trace ("hiding and freeing current splash");
ply_boot_splash_hide (state->boot_splash);
ply_boot_splash_free (state->boot_splash);
state->boot_splash = NULL;
}
if (!state->showing_details)
{
show_detailed_splash (state);
state->showing_details = true;
}
else
{
show_default_splash (state);
state->showing_details = false;
}
update_display (state);
show_messages (state);
}
static void
on_escape_pressed (state_t *state)
{
ply_trace ("escape key pressed");
toggle_between_splash_and_details (state);
}
static void
on_keyboard_input (state_t *state,
const char *keyboard_input,
size_t character_size)
{
ply_list_node_t *node;
node = ply_list_get_first_node (state->entry_triggers);
if (node)
{ /* \x3 (ETX) is Ctrl+C and \x4 (EOT) is Ctrl+D */
if (character_size == 1 && ( keyboard_input[0] == '\x3' || keyboard_input[0] == '\x4' ))
{
ply_entry_trigger_t* entry_trigger = ply_list_node_get_data (node);
ply_trigger_pull (entry_trigger->trigger, "\x3");
ply_buffer_clear (state->entry_buffer);
ply_list_remove_node (state->entry_triggers, node);
free (entry_trigger);
}
else
{
ply_buffer_append_bytes (state->entry_buffer, keyboard_input, character_size);
}
update_display (state);
}
else
{
for (node = ply_list_get_first_node (state->keystroke_triggers); node;
node = ply_list_get_next_node (state->keystroke_triggers, node))
{
ply_keystroke_watch_t* keystroke_trigger = ply_list_node_get_data (node);
if (!keystroke_trigger->keys || strstr(keystroke_trigger->keys, keyboard_input)) /* assume strstr works on utf8 arrays */
{
ply_trigger_pull (keystroke_trigger->trigger, keyboard_input);
ply_list_remove_node (state->keystroke_triggers, node);
free(keystroke_trigger);
return;
}
}
return;
}
}
static void
on_backspace (state_t *state)
{
ssize_t bytes_to_remove;
ssize_t previous_character_size;
const char *bytes;
size_t size;
ply_list_node_t *node = ply_list_get_first_node (state->entry_triggers);
if (!node) return;
bytes = ply_buffer_get_bytes (state->entry_buffer);
size = ply_buffer_get_size (state->entry_buffer);
bytes_to_remove = MIN(size, PLY_UTF8_CHARACTER_SIZE_MAX);
while ((previous_character_size = ply_utf8_character_get_size (bytes + size - bytes_to_remove, bytes_to_remove)) < bytes_to_remove)
{
if (previous_character_size > 0)
bytes_to_remove -= previous_character_size;
else
bytes_to_remove--;
}
ply_buffer_remove_bytes_at_end (state->entry_buffer, bytes_to_remove);
update_display (state);
}
static void
on_enter (state_t *state,
const char *line)
{
ply_list_node_t *node;
node = ply_list_get_first_node (state->entry_triggers);
if (node)
{
ply_entry_trigger_t* entry_trigger = ply_list_node_get_data (node);
const char* reply_text = ply_buffer_get_bytes (state->entry_buffer);
ply_trigger_pull (entry_trigger->trigger, reply_text);
ply_buffer_clear (state->entry_buffer);
ply_list_remove_node (state->entry_triggers, node);
free (entry_trigger);
update_display (state);
}
else
{
for (node = ply_list_get_first_node (state->keystroke_triggers); node;
node = ply_list_get_next_node (state->keystroke_triggers, node))
{
ply_keystroke_watch_t* keystroke_trigger = ply_list_node_get_data (node);
if (!keystroke_trigger->keys || strstr(keystroke_trigger->keys, "\n")) /* assume strstr works on utf8 arrays */
{
ply_trigger_pull (keystroke_trigger->trigger, line);
ply_list_remove_node (state->keystroke_triggers, node);
free(keystroke_trigger);
return;
}
}
return;
}
}
static void
set_keyboard (state_t *state,
ply_keyboard_t *keyboard)
{
state->keyboard = keyboard;
ply_keyboard_add_escape_handler (keyboard, (ply_keyboard_escape_handler_t)
on_escape_pressed, state);
ply_trace ("listening for keystrokes");
ply_keyboard_add_input_handler (keyboard,
(ply_keyboard_input_handler_t)
on_keyboard_input, state);
ply_trace ("listening for backspace");
ply_keyboard_add_backspace_handler (keyboard,
(ply_keyboard_backspace_handler_t)
on_backspace, state);
ply_trace ("listening for enter");
ply_keyboard_add_enter_handler (keyboard,
(ply_keyboard_enter_handler_t)
on_enter, state);
}
static void
add_display_and_keyboard_for_terminal (state_t *state,
const char *tty_name)
{
ply_text_display_t *display;
ply_keyboard_t *keyboard;
ply_trace ("adding display and keyboard for %s", tty_name);
state->terminal = ply_terminal_new (tty_name);
keyboard = ply_keyboard_new_for_terminal (state->terminal);
display = ply_text_display_new (state->terminal);
ply_list_append_data (state->text_displays, display);
set_keyboard (state, keyboard);
}
static void
add_pixel_displays_from_renderer (state_t *state,
ply_renderer_t *renderer)
{
ply_list_t *heads;
ply_list_node_t *node;
heads = ply_renderer_get_heads (renderer);
ply_trace ("Adding displays for %d heads",
ply_list_get_length (heads));
node = ply_list_get_first_node (heads);
while (node != NULL)
{
ply_list_node_t *next_node;
ply_renderer_head_t *head;
ply_pixel_display_t *display;
head = ply_list_node_get_data (node);
next_node = ply_list_get_next_node (heads, node);
display = ply_pixel_display_new (renderer, head);
ply_list_append_data (state->pixel_displays, display);
node = next_node;
}
}
static void
add_default_displays_and_keyboard (state_t *state)
{
ply_renderer_t *renderer;
ply_keyboard_t *keyboard;
ply_terminal_t *terminal;
ply_text_display_t *text_display;
ply_trace ("adding default displays and keyboard");
terminal = ply_terminal_new (state->default_tty);
renderer = ply_renderer_new (NULL, terminal);
if (!ply_renderer_open (renderer))
{
ply_trace ("could not open renderer /dev/fb");
ply_renderer_free (renderer);
ply_terminal_free (terminal);
add_display_and_keyboard_for_terminal (state, state->default_tty);
return;
}
state->terminal = terminal;
keyboard = ply_keyboard_new_for_renderer (renderer);
set_keyboard (state, keyboard);
add_pixel_displays_from_renderer (state, renderer);
text_display = ply_text_display_new (state->terminal);
ply_list_append_data (state->text_displays, text_display);
state->renderer = renderer;
}
static void
add_displays_and_keyboard_to_boot_splash (state_t *state,
ply_boot_splash_t *splash)
{
ply_list_node_t *node;
ply_trace ("setting keyboard on boot splash");
if (state->keyboard != NULL)
ply_boot_splash_set_keyboard (splash, state->keyboard);
node = ply_list_get_first_node (state->pixel_displays);
while (node != NULL)
{
ply_pixel_display_t *display;
ply_list_node_t *next_node;
display = ply_list_node_get_data (node);
next_node = ply_list_get_next_node (state->pixel_displays, node);
ply_trace ("adding pixel display on boot splash");
ply_boot_splash_add_pixel_display (splash, display);
node = next_node;
}
node = ply_list_get_first_node (state->text_displays);
while (node != NULL)
{
ply_text_display_t *display;
ply_list_node_t *next_node;
display = ply_list_node_get_data (node);
next_node = ply_list_get_next_node (state->text_displays, node);
ply_trace ("adding text display on boot splash");
ply_boot_splash_add_text_display (splash, display);
node = next_node;
}
}
static ply_boot_splash_t *
start_boot_splash (state_t *state,
const char *theme_path,
bool fall_back_if_neccessary)
{
ply_boot_splash_t *splash;
ply_boot_splash_mode_t splash_mode;
bool is_loaded;
ply_trace ("Loading boot splash theme '%s'",
theme_path);
splash = ply_boot_splash_new (theme_path,
PLYMOUTH_PLUGIN_PATH,
state->boot_buffer,
state->terminal);
is_loaded = ply_boot_splash_load (splash);
if (!is_loaded && fall_back_if_neccessary)
{
ply_trace ("Splash couldn't be loaded: %m");
ply_trace ("Loading built in splash");
is_loaded = ply_boot_splash_load_built_in (splash);
}
if (!is_loaded)
{
ply_save_errno ();
ply_boot_splash_free (splash);
ply_restore_errno ();
return NULL;
}
ply_trace ("attaching plugin to event loop");
ply_boot_splash_attach_to_event_loop (splash, state->loop);
ply_trace ("attaching progress to plugin");
ply_boot_splash_attach_progress (splash, state->progress);
add_displays_and_keyboard_to_boot_splash (state, splash);
ply_trace ("showing plugin");
if (state->mode == PLY_MODE_SHUTDOWN)
splash_mode = PLY_BOOT_SPLASH_MODE_SHUTDOWN;
else
splash_mode = PLY_BOOT_SPLASH_MODE_BOOT_UP;
if (!ply_boot_splash_show (splash, splash_mode))
{
ply_save_errno ();
ply_boot_splash_unset_keyboard (splash);
ply_boot_splash_free (splash);
ply_restore_errno ();
return NULL;
}
if (state->keyboard != NULL)
ply_keyboard_watch_for_input (state->keyboard);
update_display (state);
return splash;
}
static bool
attach_to_running_session (state_t *state)
{
ply_terminal_session_t *session;
ply_terminal_session_flags_t flags;
bool should_be_redirected;
flags = 0;
should_be_redirected = !state->no_boot_log;
if (should_be_redirected)
flags |= PLY_TERMINAL_SESSION_FLAGS_REDIRECT_CONSOLE;
if (state->session == NULL)
{
ply_trace ("creating new terminal session");
session = ply_terminal_session_new (NULL);
ply_terminal_session_attach_to_event_loop (session, state->loop);
}
else
{
session = state->session;
ply_trace ("session already created");
}
if (!ply_terminal_session_attach (session, flags,
(ply_terminal_session_output_handler_t)
on_session_output,
(ply_terminal_session_hangup_handler_t)
(should_be_redirected? on_session_hangup: NULL),
-1, state))
{
ply_save_errno ();
ply_terminal_session_free (session);
ply_buffer_free (state->boot_buffer);
state->boot_buffer = NULL;
ply_restore_errno ();
state->is_redirected = false;
state->is_attached = false;
return false;
}
state->is_redirected = should_be_redirected;
state->is_attached = true;
state->session = session;
return true;
}
static bool
get_kernel_command_line (state_t *state)
{
int fd;
const char *remaining_command_line;
char *key;
ply_trace ("opening /proc/cmdline");
fd = open ("/proc/cmdline", O_RDONLY);
if (fd < 0)
{
ply_trace ("couldn't open it: %m");
return false;
}
ply_trace ("reading kernel command line");
if (read (fd, state->kernel_command_line, sizeof (state->kernel_command_line)) < 0)
{
ply_trace ("couldn't read it: %m");
return false;
}
/* we now use plymouth.argument for kernel commandline arguments.
* It used to be plymouth:argument. This bit just rewrites all : to be .
*/
remaining_command_line = state->kernel_command_line;
while ((key = strstr (remaining_command_line, "plymouth:")) != NULL)
{
char *colon;
colon = key + strlen ("plymouth");
*colon = '.';
remaining_command_line = colon + 1;
}
ply_trace ("Kernel command line is: '%s'", state->kernel_command_line);
close (fd);
return true;
}
static void
check_verbosity (state_t *state)
{
const char *path;
ply_trace ("checking if tracing should be enabled");
path = command_line_get_string_after_prefix (state->kernel_command_line,
"plymouth.debug=file:");
if (path != NULL ||
command_line_has_argument (state->kernel_command_line, "plymouth.debug"))
{
#ifdef LOG_TO_DEBUG_FILE
int fd;
#endif
ply_trace ("tracing should be enabled!");
if (!ply_is_tracing ())
ply_toggle_tracing ();
if (path != NULL && debug_buffer_path == NULL)
{
char *end;
debug_buffer_path = strdup (path);
end = debug_buffer_path + strcspn (debug_buffer_path, " \n");
*end = '\0';
}
if (debug_buffer == NULL)
debug_buffer = ply_buffer_new ();
#ifdef LOG_TO_DEBUG_FILE
fd = open ("/dev/console", O_RDWR);
ply_logger_set_output_fd (ply_logger_get_error_default (), fd);
#endif
}
else
ply_trace ("tracing shouldn't be enabled!");
if (debug_buffer != NULL)
{
if (debug_buffer_path == NULL)
debug_buffer_path = strdup (PLYMOUTH_LOG_DIRECTORY "/plymouth-debug.log");
ply_logger_add_filter (ply_logger_get_error_default (),
(ply_logger_filter_handler_t)
on_error_message,
debug_buffer);
}
}
static void
check_logging (state_t *state)
{
ply_trace ("checking if console messages should be redirected and logged");
if (command_line_has_argument (state->kernel_command_line, "plymouth.nolog"))
{
ply_trace ("logging won't be enabled!");
state->no_boot_log = true;
}
else
{
ply_trace ("logging will be enabled!");
state->no_boot_log = false;
}
}
static void
add_display_and_keyboard_for_console (const char *console,
const char *null,
state_t *state)
{
add_display_and_keyboard_for_terminal (state, console);
}
static void
check_for_consoles (state_t *state,
const char *default_tty,
bool should_add_displays)
{
const char *console_string;
const char *remaining_command_line;
char *console;
ply_hashtable_t *consoles;
ply_trace ("checking for consoles%s",
should_add_displays? " and adding displays": "");
remaining_command_line = state->kernel_command_line;
console = NULL;
consoles = ply_hashtable_new (ply_hashtable_string_hash,
ply_hashtable_string_compare);
while ((console_string = command_line_get_string_after_prefix (remaining_command_line,
"console=")) != NULL)
{
char *end;
remaining_command_line = console_string;
state->should_force_details = true;
console = strdup (console_string);
end = strpbrk (console, " \n\t\v,");
if (end != NULL)
*end = '\0';
if (strcmp (console, "tty0") == 0 || strcmp (console, "/dev/tty0") == 0)
{
free (console);
console = strdup (default_tty);
}
ply_trace ("serial console %s found!", console);
ply_hashtable_insert (consoles, console, NULL);
remaining_command_line += strlen (console);
}
free (state->kernel_console_tty);
state->kernel_console_tty = NULL;
if (console != NULL)
state->kernel_console_tty = strdup (console);
if (should_add_displays)
{
ply_hashtable_foreach (consoles,
(ply_hashtable_foreach_func_t *)
add_display_and_keyboard_for_console,
state);
}
ply_hashtable_foreach (consoles, (ply_hashtable_foreach_func_t *) free, NULL);
ply_hashtable_free (consoles);
ply_trace ("After processing serial consoles there are now %d text displays",
ply_list_get_length (state->text_displays));
if (should_add_displays && ply_list_get_length (state->text_displays) == 0)
add_default_displays_and_keyboard (state);
}
static bool
redirect_standard_io_to_device (const char *device)
{
int fd;
char *file;
ply_trace ("redirecting stdio to %s", device);
if (strncmp (device, "/dev/", strlen ("/dev/")) == 0)
file = strdup (device);
else
asprintf (&file, "/dev/%s", device);
fd = open (file, O_RDWR | O_APPEND);
free (file);
if (fd < 0)
return false;
dup2 (fd, STDIN_FILENO);
dup2 (fd, STDOUT_FILENO);
dup2 (fd, STDERR_FILENO);
close (fd);
return true;
}
static const char *
find_fallback_tty (state_t *state)
{
static const char *tty_list[] =
{
"/dev/ttyS0",
"/dev/hvc0",
"/dev/xvc0",
"/dev/ttySG0",
NULL
};
int i;
for (i = 0; tty_list[i] != NULL; i++)
{
if (ply_character_device_exists (tty_list[i]))
return tty_list[i];
}
return state->default_tty;
}
static bool
initialize_environment (state_t *state)
{
ply_trace ("initializing minimal work environment");
if (!get_kernel_command_line (state))
return false;
check_verbosity (state);
check_logging (state);
ply_trace ("source built on %s", __DATE__);
state->keystroke_triggers = ply_list_new ();
state->entry_triggers = ply_list_new ();
state->entry_buffer = ply_buffer_new();
state->pixel_displays = ply_list_new ();
state->text_displays = ply_list_new ();
state->messages = ply_list_new ();
state->keyboard = NULL;
if (!state->default_tty)
{
if (state->mode == PLY_MODE_SHUTDOWN)
{
state->default_tty = SHUTDOWN_TTY;
}
else
state->default_tty = BOOT_TTY;
ply_trace ("checking if '%s' exists", state->default_tty);
if (!ply_character_device_exists (state->default_tty))
{
ply_trace ("nope, forcing details mode");
state->should_force_details = true;
state->default_tty = find_fallback_tty (state);
ply_trace ("going to go with '%s'", state->default_tty);
}
}
check_for_consoles (state, state->default_tty, false);
if (state->kernel_console_tty != NULL)
redirect_standard_io_to_device (state->kernel_console_tty);
else
redirect_standard_io_to_device (state->default_tty);
ply_trace ("initialized minimal work environment");
return true;
}
static void
on_error_message (ply_buffer_t *debug_buffer,
const void *bytes,
size_t number_of_bytes)
{
ply_buffer_append_bytes (debug_buffer, bytes, number_of_bytes);
}
static void
dump_debug_buffer_to_file (void)
{
int fd;
const char *bytes;
size_t size;
fd = open (debug_buffer_path,
O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd < 0)
return;
size = ply_buffer_get_size (debug_buffer);
bytes = ply_buffer_get_bytes (debug_buffer);
ply_write (fd, bytes, size);
close (fd);
}
#include <termios.h>
#include <unistd.h>
static void
on_crash (int signum)
{
struct termios term_attributes;
int fd;
fd = open ("/dev/tty1", O_RDWR | O_NOCTTY);
if (fd < 0) fd = open ("/dev/hvc0", O_RDWR | O_NOCTTY);
ioctl (fd, KDSETMODE, KD_TEXT);
tcgetattr (fd, &term_attributes);
term_attributes.c_iflag |= BRKINT | IGNPAR | ICRNL | IXON;
term_attributes.c_oflag |= OPOST;
term_attributes.c_lflag |= ECHO | ICANON | ISIG | IEXTEN;
tcsetattr (fd, TCSAFLUSH, &term_attributes);
close (fd);
if (debug_buffer != NULL)
{
dump_debug_buffer_to_file ();
sleep (30);
}
if (pid_file != NULL)
{
unlink (pid_file);
free (pid_file);
pid_file = NULL;
}
signal (signum, SIG_DFL);
raise(signum);
}
int
main (int argc,
char **argv)
{
state_t state = { 0 };
int exit_code;
bool should_help = false;
bool no_daemon = false;
bool debug = false;
bool attach_to_session;
ply_daemon_handle_t *daemon_handle = NULL;
char *mode_string = NULL;
char *tty = NULL;
state.command_parser = ply_command_parser_new ("plymouthd", "Splash server");
state.loop = ply_event_loop_get_default ();
ply_command_parser_add_options (state.command_parser,
"help", "This help message", PLY_COMMAND_OPTION_TYPE_FLAG,
"attach-to-session", "Redirect console messages from screen to log", PLY_COMMAND_OPTION_TYPE_FLAG,
"no-daemon", "Do not daemonize", PLY_COMMAND_OPTION_TYPE_FLAG,
"debug", "Output debugging information", PLY_COMMAND_OPTION_TYPE_FLAG,
"debug-file", "File to output debugging information to", PLY_COMMAND_OPTION_TYPE_STRING,
"mode", "Mode is one of: boot, shutdown", PLY_COMMAND_OPTION_TYPE_STRING,
"pid-file", "Write the pid of the daemon to a file", PLY_COMMAND_OPTION_TYPE_STRING,
"tty", "TTY to use instead of default", PLY_COMMAND_OPTION_TYPE_STRING,
NULL);
if (!ply_command_parser_parse_arguments (state.command_parser, state.loop, argv, argc))
{
char *help_string;
help_string = ply_command_parser_get_help_string (state.command_parser);
ply_error_without_new_line ("%s", help_string);
free (help_string);
return EX_USAGE;
}
ply_command_parser_get_options (state.command_parser,
"help", &should_help,
"attach-to-session", &attach_to_session,
"mode", &mode_string,
"no-daemon", &no_daemon,
"debug", &debug,
"debug-file", &debug_buffer_path,
"pid-file", &pid_file,
"tty", &tty,
NULL);
if (should_help)
{
char *help_string;
help_string = ply_command_parser_get_help_string (state.command_parser);
if (argc < 2)
fprintf (stderr, "%s", help_string);
else
printf ("%s", help_string);
free (help_string);
return 0;
}
if (debug && !ply_is_tracing ())
ply_toggle_tracing ();
if (mode_string != NULL)
{
if (strcmp (mode_string, "shutdown") == 0)
state.mode = PLY_MODE_SHUTDOWN;
else
state.mode = PLY_MODE_BOOT;
free (mode_string);
}
if (tty != NULL)
{
state.default_tty = tty;
}
if (geteuid () != 0)
{
ply_error ("plymouthd must be run as root user");
return EX_OSERR;
}
chdir ("/");
signal (SIGPIPE, SIG_IGN);
if (! no_daemon)
{
daemon_handle = ply_create_daemon (pid_file);
if (daemon_handle == NULL)
{
ply_error ("plymouthd: cannot daemonize: %m");
return EX_UNAVAILABLE;
}
}
if (debug)
debug_buffer = ply_buffer_new ();
signal (SIGABRT, on_crash);
signal (SIGSEGV, on_crash);
/* If we're shutting down we don't want to die until killed
*/
if (state.mode == PLY_MODE_SHUTDOWN)
signal (SIGTERM, SIG_IGN);
/* before do anything we need to make sure we have a working
* environment.
*/
if (!initialize_environment (&state))
{
if (errno == 0)
{
if (daemon_handle != NULL)
ply_detach_daemon (daemon_handle, 0);
return 0;
}
ply_error ("plymouthd: could not setup basic operating environment: %m");
if (daemon_handle != NULL)
ply_detach_daemon (daemon_handle, EX_OSERR);
return EX_OSERR;
}
state.boot_buffer = ply_buffer_new ();
if (attach_to_session)
{
state.should_be_attached = attach_to_session;
if (!attach_to_running_session (&state))
{
ply_trace ("could not redirect console session: %m");
if (! no_daemon)
ply_detach_daemon (daemon_handle, EX_UNAVAILABLE);
return EX_UNAVAILABLE;
}
}
state.boot_server = start_boot_server (&state);
if (state.boot_server == NULL)
{
ply_trace ("could not log bootup: %m");
if (state.is_attached)
{
ply_trace ("detaching from session");
ply_terminal_session_detach (state.session);
}
if (daemon_handle != NULL)
ply_detach_daemon (daemon_handle, EX_UNAVAILABLE);
return EX_UNAVAILABLE;
}
if (daemon_handle != NULL
&& !ply_detach_daemon (daemon_handle, 0))
{
ply_error ("plymouthd: could not tell parent to exit: %m");
return EX_UNAVAILABLE;
}
state.progress = ply_progress_new ();
ply_progress_load_cache (state.progress,
get_cache_file_for_mode (state.mode));
ply_trace ("entering event loop");
exit_code = ply_event_loop_run (state.loop);
ply_trace ("exited event loop");
ply_boot_splash_free (state.boot_splash);
state.boot_splash = NULL;
ply_command_parser_free (state.command_parser);
ply_boot_server_free (state.boot_server);
state.boot_server = NULL;
ply_trace ("freeing terminal session");
ply_terminal_session_free (state.session);
ply_buffer_free (state.boot_buffer);
ply_progress_free (state.progress);
ply_trace ("exiting with code %d", exit_code);
if (debug_buffer != NULL)
{
dump_debug_buffer_to_file ();
ply_buffer_free (debug_buffer);
}
ply_free_error_log();
return exit_code;
}
/* vim: set sts=4 ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */