コード例 #1
0
ファイル: ply-boot-server.c プロジェクト: pkt/pkt-plymouth
static void
ply_boot_connection_send_answer (ply_boot_connection_t *connection,
                                 const char            *answer)
{
  uint32_t size;

  /* splash plugin isn't able to ask for password,
   * punt to client
   */
  if (answer == NULL)
    {
      if (!ply_write (connection->fd,
                      PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NO_ANSWER,
                      strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NO_ANSWER)))
        ply_error ("could not write bytes: %m");
    }
  else
    {
      size = strlen (answer);

      if (!ply_write (connection->fd,
                      PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ANSWER,
                      strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ANSWER)) ||
          !ply_write_uint32 (connection->fd,
                             size) ||
          !ply_write (connection->fd,
                      answer, size))
          ply_error ("could not write bytes: %m");

    }

}
コード例 #2
0
static void script_parse_error (script_debug_location_t *location,
                                const char              *message)
{
  ply_error ("Parser error \"%s\" L:%d C:%d : %s\n",
             location->name,
             location->line_index,
             location->column_index,
             message);
}
コード例 #3
0
ファイル: ply-boot-server.c プロジェクト: pkt/pkt-plymouth
static void
ply_boot_connection_on_quit_complete (ply_boot_connection_t *connection)
{
  ply_trace ("quit complete");
  if (!ply_write (connection->fd,
                  PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ACK,
                  strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ACK)))
    {
      ply_error ("could not write bytes: %m");
    }
}
コード例 #4
0
ファイル: plymouth.c プロジェクト: pkt/pkt-plymouth
static void
on_disconnect (state_t *state)
{
  bool wait;
  int status = 0;

  wait = false;
  ply_command_parser_get_options (state->command_parser,
                                   "wait", &wait,
                                   NULL
                                  );

  if (! wait) {
      ply_error ("error: unexpectedly disconnected from boot status daemon");
      status = 2;
  }

  ply_trace ("disconnect");
  ply_event_loop_exit (state->loop, status);
}
コード例 #5
0
script_op_t *script_parse_file (const char *filename)
{
  script_scan_t *scan = script_scan_file (filename);

  if (!scan)
    {
      ply_error ("Parser error : Error opening file %s\n", filename);
      return NULL;
    }
  script_scan_token_t *curtoken = script_scan_get_current_token (scan);
  script_debug_location_t location = curtoken->location;
  ply_list_t *list = script_parse_op_list (scan);

  curtoken = script_scan_get_current_token (scan);
  if (curtoken->type != SCRIPT_SCAN_TOKEN_TYPE_EOF)
    {
      script_parse_error (&curtoken->location, "Unparsed characters at end of file");
      return NULL;
    }
  script_op_t *op = script_parse_new_op_block (list, &location);
  script_scan_free (scan);
  return op;
}
コード例 #6
0
ファイル: ply-boot-server.c プロジェクト: pkt/pkt-plymouth
static void
ply_boot_connection_on_request (ply_boot_connection_t *connection)
{
  ply_boot_server_t *server;
  char *command, *argument;

  assert (connection != NULL);
  assert (connection->fd >= 0);

  server = connection->server;
  assert (server != NULL);

  if (!ply_boot_connection_read_request (connection,
                                         &command, &argument))
    {
      ply_trace ("could not read connection request");
      return;
    }

  if (!ply_boot_connection_is_from_root (connection))
    {
      ply_error ("request came from non-root user");

      if (!ply_write (connection->fd,
                      PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NAK,
                      strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NAK)))
        ply_error ("could not write bytes: %m");

      free (command);
      return;
    }

  if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_UPDATE) == 0)
    {
      ply_trace ("got update request");
      if (server->update_handler != NULL)
        server->update_handler (server->user_data, argument, server);
      free (argument);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_SYSTEM_INITIALIZED) == 0)
    {
      ply_trace ("got system initialized notification");
      if (server->system_initialized_handler != NULL)
        server->system_initialized_handler (server->user_data, server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_ERROR) == 0)
    {
      ply_trace ("got error notification");
      if (server->error_handler != NULL)
        server->error_handler (server->user_data, server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_SHOW_SPLASH) == 0)
    {
      ply_trace ("got show splash request");
      if (server->show_splash_handler != NULL)
        server->show_splash_handler (server->user_data, server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_HIDE_SPLASH) == 0)
    {
      ply_trace ("got hide splash request");
      if (server->hide_splash_handler != NULL)
        server->hide_splash_handler (server->user_data, server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_DEACTIVATE) == 0)
    {
      ply_trigger_t *deactivate_trigger;

      ply_trace ("got deactivate request");

      deactivate_trigger = ply_trigger_new (NULL);

      ply_trigger_add_handler (deactivate_trigger,
                               (ply_trigger_handler_t)
                               ply_boot_connection_on_deactivated,
                               connection);

      if (server->deactivate_handler != NULL)
        server->deactivate_handler (server->user_data, deactivate_trigger, server);

      free (argument);
      free (command);
      return;
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_REACTIVATE) == 0)
    {
      ply_trace ("got reactivate request");
      if (server->reactivate_handler != NULL)
        server->reactivate_handler (server->user_data, server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_QUIT) == 0)
    {
      bool retain_splash;
      ply_trigger_t *quit_trigger;

      retain_splash = (bool) argument[0];

      ply_trace ("got quit %srequest", retain_splash? "--retain-splash " : "");

      quit_trigger = ply_trigger_new (NULL);

      ply_trigger_add_handler (quit_trigger,
                               (ply_trigger_handler_t)
                               ply_boot_connection_on_quit_complete,
                               connection);

      if (server->quit_handler != NULL)
        server->quit_handler (server->user_data, retain_splash, quit_trigger, server);

      free(argument);
      free(command);
      return;
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_PASSWORD) == 0)
    {
      ply_trigger_t *answer;

      ply_trace ("got password request");

      answer = ply_trigger_new (NULL);
      ply_trigger_add_handler (answer,
                               (ply_trigger_handler_t)
                               ply_boot_connection_on_password_answer,
                               connection);

      if (server->ask_for_password_handler != NULL)
        server->ask_for_password_handler (server->user_data,
                                          argument,
                                          answer,
                                          server);
      /* will reply later
       */
      free(command);
      return;
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_CACHED_PASSWORD) == 0)
    {
      ply_list_node_t *node;
      ply_buffer_t *buffer;
      size_t buffer_size;
      uint32_t size;

      ply_trace ("got cached password request");

      buffer = ply_buffer_new ();

      node = ply_list_get_first_node (server->cached_passwords);

      ply_trace ("There are %d cached passwords",
                 ply_list_get_length (server->cached_passwords));

      /* Add each answer separated by their NUL terminators into
       * a buffer that we write out to the client
       */
      while (node != NULL)
        {
          ply_list_node_t *next_node;
          const char *password;

          next_node = ply_list_get_next_node (server->cached_passwords, node);
          password = (const char *) ply_list_node_get_data (node);

          ply_buffer_append_bytes (buffer,
                                   password,
                                   strlen (password) + 1);
          node = next_node;
        }

      buffer_size = ply_buffer_get_size (buffer);

      /* splash plugin doesn't have any cached passwords
      */
      if (buffer_size == 0)
        {
          if (!ply_write (connection->fd,
                          PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NO_ANSWER,
                          strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NO_ANSWER)))
              ply_error ("could not write bytes: %m");
        }
      else
        {
          size = buffer_size;

          ply_trace ("writing %d cached answers",
                     ply_list_get_length (server->cached_passwords));
          if (!ply_write (connection->fd,
                          PLY_BOOT_PROTOCOL_RESPONSE_TYPE_MULTIPLE_ANSWERS,
                          strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_MULTIPLE_ANSWERS)) ||
              !ply_write_uint32 (connection->fd,
                                 size) ||
              !ply_write (connection->fd,
                          ply_buffer_get_bytes (buffer), size))
              ply_error ("could not write bytes: %m");
        }

      ply_buffer_free (buffer);
      free(command);
      return;
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_QUESTION) == 0)
    {
      ply_trigger_t *answer;

      ply_trace ("got question request");

      answer = ply_trigger_new (NULL);
      ply_trigger_add_handler (answer,
                               (ply_trigger_handler_t)
                               ply_boot_connection_on_question_answer,
                               connection);

      if (server->ask_question_handler != NULL)
        server->ask_question_handler (server->user_data,
                                          argument,
                                          answer,
                                          server);
      /* will reply later
       */
      free(command);
      return;
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_MESSAGE) == 0)
    {
      ply_trace ("got message request");
      if (server->display_message_handler != NULL)
        server->display_message_handler(server->user_data, argument, server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_KEYSTROKE) == 0)
    {
      ply_trigger_t *answer;

      ply_trace ("got keystroke request");

      answer = ply_trigger_new (NULL);
      ply_trigger_add_handler (answer,
                               (ply_trigger_handler_t)
                               ply_boot_connection_on_keystroke_answer,
                               connection);

      if (server->watch_for_keystroke_handler != NULL)
        server->watch_for_keystroke_handler (server->user_data,
                                          argument,
                                          answer,
                                          server);
      /* will reply later
       */
      free(command);
      return;
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_KEYSTROKE_REMOVE) == 0)
    {
      ply_trace ("got keystroke remove request");
      if (server->ignore_keystroke_handler != NULL)
        server->ignore_keystroke_handler (server->user_data,
                                          argument,
                                          server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_PROGRESS_PAUSE) == 0)
    {
      ply_trace ("got progress pause request");
      if (server->progress_pause_handler != NULL)
        server->progress_pause_handler (server->user_data,
                                        server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_PROGRESS_UNPAUSE) == 0)
    {
      ply_trace ("got progress unpause request");
      if (server->progress_unpause_handler != NULL)
        server->progress_unpause_handler (server->user_data,
                                          server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_NEWROOT) == 0)
    {
      ply_trace ("got newroot request");
      if (server->newroot_handler != NULL)
        server->newroot_handler(server->user_data, argument, server);
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_HAS_ACTIVE_VT) == 0)
    {
      bool answer = false;

      ply_trace ("got has_active vt? request");
      if (server->has_active_vt_handler != NULL)
        answer = server->has_active_vt_handler(server->user_data, server);

      if (!answer)
        {
          if (!ply_write (connection->fd,
                          PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NAK,
                          strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NAK)))
            ply_error ("could not write bytes: %m");

          free(command);
          return;
        }
    }
  else if (strcmp (command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_PING) != 0)
    {
      ply_error ("received unknown command '%s' from client", command);

      if (!ply_write (connection->fd,
                      PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NAK,
                      strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NAK)))
        ply_error ("could not write bytes: %m");

      free(command);
      return;
    }

  if (!ply_write (connection->fd, 
                  PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ACK,
                  strlen (PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ACK)))
    {
      ply_error ("could not write bytes: %m");
    }
  free(command);
}
コード例 #7
0
ファイル: ply-boot-client.c プロジェクト: pkt/pkt-plymouth
static void
ply_boot_client_process_incoming_replies (ply_boot_client_t *client)
{
  ply_list_node_t *request_node;
  ply_boot_client_request_t *request;
  bool processed_reply;
  uint8_t byte[2] = "";
  uint32_t size;

  assert (client != NULL);

  processed_reply = false;
  if (ply_list_get_length (client->requests_waiting_for_replies) == 0)
    {
      ply_error ("received unexpected response from boot status daemon");
      return;
    }

  if (!ply_read (client->socket_fd, byte, sizeof (uint8_t)))
    goto out;

  for (request_node = ply_list_get_first_node (client->requests_waiting_for_replies);
       request_node; request_node = ply_list_get_next_node (client->requests_waiting_for_replies, request_node))
    {
      assert (request_node != NULL);
      request = (ply_boot_client_request_t *) ply_list_node_get_data (request_node);
      assert (request != NULL);

      if (! strcmp (request->command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_PASSWORD)
          || ! strcmp (request->command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_QUESTION)
          || ! strcmp (request->command, PLY_BOOT_PROTOCOL_REQUEST_TYPE_KEYSTROKE))
        {
          if (! memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ANSWER, sizeof (uint8_t))
              || ! memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NO_ANSWER, sizeof (uint8_t)))
            break;
        }
      else
        {
          if (memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ANSWER, sizeof (uint8_t))
              && memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NO_ANSWER, sizeof (uint8_t)))
            break;
        }
    }

  if (memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ACK, sizeof (uint8_t)) == 0)
      request->handler (request->user_data, client);
  else if (memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_ANSWER, sizeof (uint8_t)) == 0)
    {
      char *answer;

      if (!ply_read_uint32 (client->socket_fd, &size))
        goto out;
      
      answer = malloc ((size+1) * sizeof(char));
      if (size > 0)
        {
          if (!ply_read (client->socket_fd, answer, size))
            goto out;
        }

      answer[size] = '\0';
      ((ply_boot_client_answer_handler_t) request->handler) (request->user_data, answer, client);
      free(answer);
    }
  else if (memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_MULTIPLE_ANSWERS, sizeof (uint8_t)) == 0)
    {
      ply_array_t *array;
      char **answers;
      char *answer;
      char *p;
      char *q;
      uint8_t i;

      array = NULL;
      answers = NULL;

      if (!ply_read_uint32 (client->socket_fd, &size))
        goto out;

      assert (size > 0);

      answer = malloc (size);

      if (!ply_read (client->socket_fd, answer, size))
        {
          free (answer);
          goto out;
        }

      array = ply_array_new ();

      p = answer;
      q = p;
      for (i = 0; i < size; i++, q++)
        {
          if (*q == '\0')
            {
              ply_array_add_element (array, strdup (p));
              p = q + 1;
            }
        }
      free (answer);

      answers = (char **) ply_array_steal_elements (array);
      ply_array_free (array);

      ((ply_boot_client_multiple_answers_handler_t) request->handler) (request->user_data, (const char * const *) answers, client);

      ply_free_string_array (answers);
    }
  else if (memcmp (byte, PLY_BOOT_PROTOCOL_RESPONSE_TYPE_NO_ANSWER, sizeof (uint8_t)) == 0)
    {
      ((ply_boot_client_answer_handler_t) request->handler) (request->user_data, NULL, client);
    }
  else
    goto out;

  processed_reply = true;

out:
  if (!processed_reply)
    {
      if (request->failed_handler != NULL)
        request->failed_handler (request->user_data, client);
    }

  ply_list_remove_node (client->requests_waiting_for_replies, request_node);

  if (ply_list_get_length (client->requests_waiting_for_replies) == 0)
    {
      if (client->daemon_has_reply_watch != NULL)
        {
          assert (client->loop != NULL);
          ply_event_loop_stop_watching_fd (client->loop,
                                           client->daemon_has_reply_watch);
          client->daemon_has_reply_watch = NULL;
        }
    }
}
コード例 #8
0
int
main (int    argc,
      char **argv)
{
  state_t state = { 0 };
  bool should_help, should_be_verbose;
  bool is_connected;
  int exit_code;

  exit_code = 0;

  signal (SIGPIPE, SIG_IGN);

  state.loop = ply_event_loop_new ();
  state.client = ply_boot_client_new ();
  state.command_parser = ply_command_parser_new ("plymouth-upstart-bridge", "Upstart job state bridge");

  ply_command_parser_add_options (state.command_parser,
                                  "help", "This help message", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "debug", "Enable verbose debug logging", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  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 ("%s", help_string);

      free (help_string);
      return 1;
    }

  ply_command_parser_get_options (state.command_parser,
                                  "help", &should_help,
                                  "debug", &should_be_verbose,
                                  NULL);

  if (should_help)
    {
      char *help_string;

      help_string = ply_command_parser_get_help_string (state.command_parser);

      puts (help_string);

      free (help_string);
      return 0;
    }

  if (should_be_verbose && !ply_is_tracing ())
    ply_toggle_tracing ();

  setupterm (NULL, STDOUT_FILENO, NULL);

  is_connected = ply_boot_client_connect (state.client,
                                          (ply_boot_client_disconnect_handler_t)
                                          on_disconnect, &state);
  if (!is_connected)
    {
      ply_trace ("daemon not running");
      return 1;
    }

  ply_boot_client_attach_to_event_loop (state.client, state.loop);
  state.upstart = ply_upstart_monitor_new (state.loop);
  if (!state.upstart)
    return 1;
  ply_upstart_monitor_add_state_changed_handler (state.upstart,
                                                 (ply_upstart_monitor_state_changed_handler_t)
                                                 on_state_changed, &state);
  ply_upstart_monitor_add_failed_handler (state.upstart, on_failed, &state);

  exit_code = ply_event_loop_run (state.loop);

  ply_upstart_monitor_free (state.upstart);
  ply_boot_client_free (state.client);

  ply_event_loop_free (state.loop);

  return exit_code;
}
コード例 #9
0
ファイル: plymouth.c プロジェクト: pkt/pkt-plymouth
int
main (int    argc,
      char **argv)
{
  state_t state = { 0 };
  bool should_help, should_quit, should_ping, should_check_for_active_vt, should_sysinit, should_ask_for_password, should_show_splash, should_hide_splash, should_wait, should_be_verbose, report_error, should_get_plugin_path;
  bool is_connected;
  char *status, *chroot_dir, *ignore_keystroke;
  int exit_code;

  exit_code = 0;

  signal (SIGPIPE, SIG_IGN);

  state.loop = ply_event_loop_new ();
  state.client = ply_boot_client_new ();
  state.command_parser = ply_command_parser_new ("plymouth", "Boot splash control client");

  ply_command_parser_add_options (state.command_parser,
                                  "help", "This help message", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "debug", "Enable verbose debug logging", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "get-splash-plugin-path", "Get directory where splash plugins are installed", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "newroot", "Tell boot daemon that new root filesystem is mounted", PLY_COMMAND_OPTION_TYPE_STRING,
                                  "quit", "Tell boot daemon to quit", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "ping", "Check of boot daemon is running", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "has-active-vt", "Check if boot daemon has an active vt", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "sysinit", "Tell boot daemon root filesystem is mounted read-write", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "show-splash", "Show splash screen", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "hide-splash", "Hide splash screen", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "ask-for-password", "Ask user for password", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "ignore-keystroke", "Remove sensitivity to a keystroke", PLY_COMMAND_OPTION_TYPE_STRING,
                                  "update", "Tell boot daemon an update about boot progress", PLY_COMMAND_OPTION_TYPE_STRING,
                                  "details", "Tell boot daemon there were errors during boot", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "wait", "Wait for boot daemon to quit", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "update", "Tell daemon about boot status changes",
                                  (ply_command_handler_t)
                                  on_update_request, &state,
                                  "status", "Tell daemon the current boot status",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "update-root-fs", "Tell daemon about root filesystem changes",
                                  (ply_command_handler_t)
                                  on_update_root_fs_request, &state,
                                  "new-root-dir", "Root filesystem is about to change",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  "read-write", "Root filesystem is no longer read-only",
                                  PLY_COMMAND_OPTION_TYPE_FLAG,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "show-splash", "Tell daemon to show splash screen",
                                  (ply_command_handler_t)
                                  on_show_splash_request, &state,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "hide-splash", "Tell daemon to hide splash screen",
                                  (ply_command_handler_t)
                                  on_hide_splash_request, &state,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "ask-for-password", "Ask user for password",
                                  (ply_command_handler_t)
                                  on_password_request, &state,
                                  "command", "Command to send password to via standard input",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  "prompt", "Message to display when asking for password",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  "number-of-tries", "Number of times to ask before giving up (requires --command)",
                                  PLY_COMMAND_OPTION_TYPE_INTEGER,
                                  "dont-pause-progress", "Don't pause boot progress bar while asking",
                                  PLY_COMMAND_OPTION_TYPE_FLAG,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "ask-question", "Ask user a question",
                                  (ply_command_handler_t)
                                  on_question_request, &state,
                                  "command", "Command to send the answer to via standard input",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  "prompt", "Message to display when asking the question",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  "dont-pause-progress", "Don't pause boot progress bar while asking",
                                  PLY_COMMAND_OPTION_TYPE_FLAG,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "message", "Display a message",
                                  (ply_command_handler_t)
                                  on_message_request, &state,
                                  "text", "The message text",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "watch-keystroke", "Become sensitive to a keystroke",
                                  (ply_command_handler_t)
                                  on_keystroke_request, &state,
                                  "command", "Command to send keystroke to via standard input",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  "keys", "Keys to become sensitive to",
                                  PLY_COMMAND_OPTION_TYPE_STRING,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "pause-progress", "Pause boot progress bar",
                                  (ply_command_handler_t)
                                  on_progress_pause_request, &state,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "unpause-progress", "Unpause boot progress bar",
                                  (ply_command_handler_t)
                                  on_progress_unpause_request, &state,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "report-error", "Tell boot daemon there were errors during boot",
                                  (ply_command_handler_t)
                                  on_report_error_request, &state,
                                  NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "deactivate", "Tell boot daemon to deactivate",
                                  (ply_command_handler_t)
                                  on_deactivate_request, &state, NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "reactivate", "Tell boot daemon to reactivate",
                                  (ply_command_handler_t)
                                  on_reactivate_request, &state, NULL);

  ply_command_parser_add_command (state.command_parser,
                                  "quit", "Tell boot daemon to quit",
                                  (ply_command_handler_t)
                                  on_quit_request, &state,
                                  "retain-splash", "Don't explicitly hide boot splash on exit",
                                  PLY_COMMAND_OPTION_TYPE_FLAG, 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 ("%s", help_string);

      free (help_string);
      return 1;
    }

  ply_command_parser_get_options (state.command_parser,
                                  "help", &should_help,
                                  "debug", &should_be_verbose,
                                  "get-splash-plugin-path", &should_get_plugin_path,
                                  "newroot", &chroot_dir,
                                  "quit", &should_quit,
                                  "ping", &should_ping,
                                  "has-active-vt", &should_check_for_active_vt,
                                  "sysinit", &should_sysinit,
                                  "show-splash", &should_show_splash,
                                  "hide-splash", &should_hide_splash,
                                  "ask-for-password", &should_ask_for_password,
                                  "ignore-keystroke", &ignore_keystroke,
                                  "update", &status,
                                  "wait", &should_wait,
                                  "details", &report_error,
                                  NULL);

  if (should_help || argc < 2)
    {
      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 (get_kernel_command_line (&state))
    {
      if (strstr (state.kernel_command_line, "plymouth:debug") != NULL
          && !ply_is_tracing ())
        ply_toggle_tracing ();
    }

  if (should_be_verbose && !ply_is_tracing ())
    ply_toggle_tracing ();

  if (should_get_plugin_path)
    {
      printf ("%s\n", PLYMOUTH_PLUGIN_PATH);
      return 0;
    }

  is_connected = ply_boot_client_connect (state.client,
                                          (ply_boot_client_disconnect_handler_t)
                                          on_disconnect, &state);
  if (!is_connected)
    {
      ply_trace ("daemon not running");

      if (should_ping)
        {
          ply_trace ("ping failed");
          return 1;
        }
      if (should_check_for_active_vt)
        {
          ply_trace ("has active vt? failed");
          return 1;
        }
    }

  ply_boot_client_attach_to_event_loop (state.client, state.loop);

  if (should_show_splash)
    ply_boot_client_tell_daemon_to_show_splash (state.client,
                                               (ply_boot_client_response_handler_t)
                                               on_success,
                                               (ply_boot_client_response_handler_t)
                                               on_failure, &state);
  else if (should_hide_splash)
    ply_boot_client_tell_daemon_to_hide_splash (state.client,
                                               (ply_boot_client_response_handler_t)
                                               on_success,
                                               (ply_boot_client_response_handler_t)
                                               on_failure, &state);
  else if (should_quit)
    ply_boot_client_tell_daemon_to_quit (state.client,
                                         false,
                                         (ply_boot_client_response_handler_t)
                                         on_success,
                                         (ply_boot_client_response_handler_t)
                                         on_failure, &state);
  else if (should_ping)
    ply_boot_client_ping_daemon (state.client,
                                 (ply_boot_client_response_handler_t)
                                 on_success, 
                                 (ply_boot_client_response_handler_t)
                                 on_failure, &state);
  else if (should_check_for_active_vt)
    ply_boot_client_ask_daemon_has_active_vt (state.client,
                                              (ply_boot_client_response_handler_t)
                                              on_success,
                                              (ply_boot_client_response_handler_t)
                                              on_failure, &state);
  else if (status != NULL)
    ply_boot_client_update_daemon (state.client, status,
                                   (ply_boot_client_response_handler_t)
                                   on_success, 
                                   (ply_boot_client_response_handler_t)
                                   on_failure, &state);
  else if (should_ask_for_password)
    {
      password_answer_state_t answer_state = { 0 };

      answer_state.state = &state;
      answer_state.number_of_tries_left = 1;
      ply_boot_client_ask_daemon_for_password (state.client,
                                               NULL,
                                               (ply_boot_client_answer_handler_t)
                                               on_password_answer,
                                               (ply_boot_client_response_handler_t)
                                               on_password_answer_failure, &answer_state);
    }
  else if (ignore_keystroke)
    {
      ply_boot_client_ask_daemon_to_ignore_keystroke (state.client,
                                           ignore_keystroke,
                                           (ply_boot_client_answer_handler_t)
                                           on_success,
                                           (ply_boot_client_response_handler_t)
                                           on_failure, &state);
    }
  else if (should_sysinit)
    ply_boot_client_tell_daemon_system_is_initialized (state.client,
                                   (ply_boot_client_response_handler_t)
                                   on_success, 
                                   (ply_boot_client_response_handler_t)
                                   on_failure, &state);
  else if (chroot_dir)
    ply_boot_client_tell_daemon_to_change_root (state.client, chroot_dir,
                                   (ply_boot_client_response_handler_t)
                                   on_success,
                                   (ply_boot_client_response_handler_t)
                                   on_failure, &state);

  else if (should_wait)
    {} // Do nothing
  else if (report_error)
    ply_boot_client_tell_daemon_about_error (state.client,
                                             (ply_boot_client_response_handler_t)
                                             on_success,
                                             (ply_boot_client_response_handler_t)
                                             on_failure, &state);

  exit_code = ply_event_loop_run (state.loop);

  ply_boot_client_free (state.client);

  ply_event_loop_free (state.loop);

  return exit_code;
}