summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
authorStefan Walter <stefw@src.gnome.org>2008-12-11 23:33:20 +0000
committerStefan Walter <stefw@src.gnome.org>2008-12-11 23:33:20 +0000
commite458b511e09377004cf6375b731865ac01990ad9 (patch)
treed3534b602dced507fb7ba05809509e28a06c7033 /daemon
parentca9140424a21b53d9b45330b7587c6e85e7cdb5e (diff)
Rework initialization of the daemon so that most initialization can happen
* common/gkr-crypto.c: * common/gkr-secure-memory.c: * common/gkr-secure-memory.h: * daemon/gkr-daemon.c: * daemon/gkr-daemon.h: * daemon/gkr-daemon-dbus.c: * daemon/gkr-daemon-ops.c: * pam/gkr-pam-module.c: Rework initialization of the daemon so that most initialization can happen after starting via PAM. Fixes bug #558181 svn path=/trunk/; revision=1381
Diffstat (limited to 'daemon')
-rw-r--r--daemon/gkr-daemon-dbus.c13
-rw-r--r--daemon/gkr-daemon-ops.c7
-rw-r--r--daemon/gkr-daemon.c356
-rw-r--r--daemon/gkr-daemon.h6
4 files changed, 217 insertions, 165 deletions
diff --git a/daemon/gkr-daemon-dbus.c b/daemon/gkr-daemon-dbus.c
index 982b663c..d0726ce4 100644
--- a/daemon/gkr-daemon-dbus.c
+++ b/daemon/gkr-daemon-dbus.c
@@ -202,7 +202,7 @@ register_environment_in_session (void)
dbus_message_unref (msg);
if (!reply) {
- g_warning ("couldn't set environment variable in session: %s", derr.message);
+ g_message ("couldn't set environment variable in session: %s", derr.message);
return;
}
@@ -357,15 +357,6 @@ gkr_daemon_dbus_setup (void)
DBusError derr = { 0 };
const gchar *env;
- /*
- * We may be launched before the DBUS session, (ie: via PAM)
- * and DBus tries to launch itself somehow, so double check
- * that it has really started.
- */
- env = getenv ("DBUS_SESSION_BUS_ADDRESS");
- if (!env || !env[0])
- return;
-
if (dbus_initialized)
return;
@@ -414,7 +405,7 @@ gkr_daemon_dbus_setup (void)
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
case DBUS_REQUEST_NAME_REPLY_EXISTS:
g_message ("another gnome-keyring-daemon is running");
- return;
+ break;
default:
g_return_if_reached ();
break;
diff --git a/daemon/gkr-daemon-ops.c b/daemon/gkr-daemon-ops.c
index b7121283..c20b9d89 100644
--- a/daemon/gkr-daemon-ops.c
+++ b/daemon/gkr-daemon-ops.c
@@ -1713,8 +1713,11 @@ op_prepare_daemon_environment (GkrBuffer *packet, GkrBuffer *result, GkrKeyringR
g_strfreev (environment);
- /* We may have received DBUS environment variable so try and setup DBUS */
- gkr_daemon_dbus_setup ();
+ /*
+ * We've now definitely received everything we need to run. Ask
+ * the daemon to complete the initialization.
+ */
+ gkr_daemon_complete_initialization();
gkr_buffer_add_uint32 (result, GNOME_KEYRING_RESULT_OK);
diff --git a/daemon/gkr-daemon.c b/daemon/gkr-daemon.c
index 08960a3a..c1966a4f 100644
--- a/daemon/gkr-daemon.c
+++ b/daemon/gkr-daemon.c
@@ -90,28 +90,30 @@ static GMainLoop *loop = NULL;
/* All the components to run on startup if not set in gconf */
#ifdef WITH_SSH
-#define DEFAULT_COMPONENTS "ssh,keyring,pkcs11"
+#define DEFAULT_COMPONENTS "ssh,pkcs11"
#else
-#define DEFAULT_COMPONENTS "keyring,pkcs11"
+#define DEFAULT_COMPONENTS "pkcs11"
#endif
static gboolean run_foreground = FALSE;
static gboolean run_daemonized = FALSE;
-static gboolean unlock_with_login = FALSE;
-static gboolean start_any_daemon = FALSE;
+static gboolean run_for_login = FALSE;
+static gboolean run_for_start = FALSE;
static gchar* run_components = NULL;
+static gchar* login_password = NULL;
+static gboolean initialization_completed = FALSE;
static GOptionEntry option_entries[] = {
{ "foreground", 'f', 0, G_OPTION_ARG_NONE, &run_foreground,
"Run in the foreground", NULL },
{ "daemonize", 'd', 0, G_OPTION_ARG_NONE, &run_daemonized,
"Run as a daemon", NULL },
- { "login", 'l', 0, G_OPTION_ARG_NONE, &unlock_with_login,
- "Use login password from stdin", NULL },
- { "start", 's', 0, G_OPTION_ARG_NONE, &start_any_daemon,
- "Start or initialize an already running daemon" },
+ { "login", 'l', 0, G_OPTION_ARG_NONE, &run_for_login,
+ "Run for a user login. Read login password from stdin", NULL },
+ { "start", 's', 0, G_OPTION_ARG_NONE, &run_for_start,
+ "Start a dameon or initialize an already running daemon." },
{ "components", 'c', 0, G_OPTION_ARG_STRING, &run_components,
- "The components to run", DEFAULT_COMPONENTS },
+ "The optional components to run", DEFAULT_COMPONENTS },
{ NULL }
};
@@ -136,9 +138,9 @@ parse_arguments (int *argc, char** argv[])
}
/* Check the arguments */
- if (unlock_with_login && start_any_daemon) {
+ if (run_for_login && run_for_start) {
g_printerr ("gnome-keyring-daemon: The --start option is incompatible with --login");
- start_any_daemon = FALSE;
+ run_for_login = FALSE;
}
@@ -369,6 +371,12 @@ read_login_password (int fd)
/* We only accept a max of 8K as the login password */
#define MAX_LENGTH 8192
#define MAX_BLOCK 256
+
+ /*
+ * When --login is specified then the login password is passed
+ * in on stdin. All data (including newlines) are part of the
+ * password.
+ */
gchar *buf = gkr_secure_alloc (MAX_BLOCK);
gchar *ret = NULL;
@@ -401,28 +409,18 @@ read_login_password (int fd)
}
static void
-close_stdinout (void)
+cleanup_and_exit (int code)
{
- int fd;
-
- fd = open ("/dev/null", O_RDONLY);
- sane_dup2 (fd, 0);
- close (fd);
-
- fd = open ("/dev/null", O_WRONLY);
- sane_dup2 (fd, 1);
- close (fd);
-
- fd = open ("/dev/null", O_WRONLY);
- sane_dup2 (fd, 2);
- close (fd);
+ gkr_cleanup_perform ();
+ exit (code);
}
static void
-cleanup_and_exit (int code)
+clear_login_password (void)
{
- gkr_cleanup_perform ();
- _exit (code);
+ if(login_password)
+ gkr_secure_strfree (login_password);
+ login_password = NULL;
}
static gboolean
@@ -430,11 +428,32 @@ lifetime_slave_pipe_io (GIOChannel *channel,
GIOCondition cond,
gpointer callback_data)
{
- cleanup_and_exit (2);
+ gkr_cleanup_perform ();
+ _exit (2);
return FALSE;
}
static void
+slave_lifetime_to_fd (void)
+{
+ const char *env;
+ GIOChannel *channel;
+ int fd;
+
+ env = getenv ("GNOME_KEYRING_LIFETIME_FD");
+ if (env && env[0]) {
+ fd = atoi (env);
+ if (fd != 0) {
+ channel = g_io_channel_unix_new (fd);
+ g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP,
+ lifetime_slave_pipe_io, NULL);
+ g_io_channel_unref (channel);
+ }
+ }
+}
+
+static void
print_environment (pid_t pid)
{
const gchar **env;
@@ -445,7 +464,7 @@ print_environment (pid_t pid)
}
static gboolean
-initialize_running_daemon (int sock)
+initialize_other_running_daemon (int sock)
{
GnomeKeyringResult res;
gchar **envp, **e;
@@ -506,11 +525,11 @@ start_or_initialize_daemon (void)
* a daemon process, just return and let things go
* their normal way.
*/
- sock = gnome_keyring_socket_connect_daemon (FALSE);
+ sock = gnome_keyring_socket_connect_daemon (FALSE, TRUE);
if (sock == -1)
return FALSE;
- ret = initialize_running_daemon (sock);
+ ret = initialize_other_running_daemon (sock);
close (sock);
/* Initialization failed, start this process up as a daemon */
@@ -532,147 +551,194 @@ start_or_initialize_daemon (void)
return TRUE;
}
-int
-main (int argc, char *argv[])
+static void
+fork_and_print_environment (void)
{
- const char *env;
- int fd;
+ int status;
pid_t pid;
- GIOChannel *channel;
- GMainContext *ctx;
- gchar *login_password;
- unsigned seed;
+ int fd, i;
+
+ if (run_foreground) {
+ print_environment (getpid ());
+ return;
+ }
- g_type_init ();
- g_thread_init (NULL);
+ pid = fork ();
+
+ if (pid != 0) {
+
+ /* Here we are in the initial process */
+
+ if (run_daemonized) {
+
+ /* Initial process, waits for intermediate child */
+ if (pid == -1)
+ exit (1);
+
+ waitpid (pid, &status, 0);
+ if (WEXITSTATUS (status) != 0)
+ exit (WEXITSTATUS (status));
+
+ } else {
+ /* Not double forking, we know the PID */
+ print_environment (pid);
+ }
+
+ /* The initial process exits successfully */
+ exit (0);
+ }
- parse_arguments (&argc, &argv);
+ if (!run_daemonized)
+ return;
-#ifdef HAVE_LOCALE_H
- /* internationalisation */
- setlocale (LC_ALL, "");
-#endif
+ /* Double fork if need to daemonize properly */
+ pid = fork ();
+
+ if (pid != 0) {
-#ifdef HAVE_GETTEXT
- bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
- textdomain (GETTEXT_PACKAGE);
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
+ /* Here we are in the intermediate child process */
+
+ /*
+ * This process exits, so that the final child will inherit
+ * init as parent to avoid zombies
+ */
+ if (pid == -1)
+ exit (1);
+
+ /* We've done two forks. Now we know the PID */
+ print_environment (pid);
+
+ /* The intermediate child exits */
+ exit (0);
+ }
- /*
- * If asked to start a daemon, see if one is already running
- * and initialize it if so.
+ /* Here we are in the resulting daemon process. */
+
+ for (i = 0; i < 3; ++i) {
+ fd = open ("/dev/null", O_RDONLY);
+ sane_dup2 (fd, i);
+ close (fd);
+ }
+}
+
+gboolean
+gkr_daemon_complete_initialization(void)
+{
+ /*
+ * Sometimes we don't initialize the full daemon right on
+ * startup. When run with --login is one such case.
*/
- if (start_any_daemon) {
- if (start_or_initialize_daemon ())
- return 0;
+
+ if (initialization_completed) {
+ g_message ("The daemon was already initialized.");
+ return TRUE;
}
- gkr_crypto_setup ();
-
- gcry_create_nonce (&seed, sizeof (seed));
- srand (seed);
+ gkr_daemon_dbus_setup ();
/* Initialize object storage */
if (!gkr_pk_object_storage_initialize ())
- cleanup_and_exit (1);
+ return FALSE;
#ifdef ROOT_CERTIFICATES
if (!gkr_pk_root_storage_initialize ())
- cleanup_and_exit (1);
+ return FALSE;
#endif
/* Initialize the appropriate components */
- if (check_run_component ("keyring")) {
- if (!gkr_daemon_io_create_master_socket ())
- cleanup_and_exit (1);
- }
-
+
#ifdef WITH_SSH
if (check_run_component ("ssh")) {
if (!gkr_daemon_ssh_io_initialize () ||
!gkr_ssh_storage_initialize ())
- cleanup_and_exit (1);
+ return FALSE;
}
#endif
if (check_run_component ("pkcs11")) {
if (!gkr_pkcs11_daemon_setup ())
- cleanup_and_exit (1);
- }
-
+ return FALSE;
+ }
+
+ initialization_completed = TRUE;
+ return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GMainContext *ctx;
+
/*
- * When --login is specified then the login password is passed
- * in on stdin. All data (including newlines) are part of the
- * password.
+ * The gnome-keyring startup is not as simple as I wish it could be.
+ *
+ * It's often started in the primidoral stages of a session, where
+ * there's no DBus, no GConf, and no proper X display. This is the
+ * strange world of PAM.
+ *
+ * When started with the --login option, we do as little initialization
+ * as possible. We expect a login password on the stdin, and unlock
+ * or create the login keyring.
+ *
+ * Then later we expect gnome-keyring-dameon to be run again with the
+ * --start option. This second gnome-keyring-daemon will hook the
+ * original daemon up with environment variables necessary to initialize
+ * itself and bring it into the session. This second daemon usually exits.
+ *
+ * Without either of these options, we follow a more boring and
+ * predictable startup.
*/
- login_password = unlock_with_login ? read_login_password (STDIN) : NULL;
+
+ g_type_init ();
+ g_thread_init (NULL);
+
+#ifdef HAVE_LOCALE_H
+ /* internationalisation */
+ setlocale (LC_ALL, "");
+#endif
+
+#ifdef HAVE_GETTEXT
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+ gkr_crypto_setup ();
+
+ /* Send all warning or error messages to syslog */
+ prepare_logging ();
+
+ parse_arguments (&argc, &argv);
+
+ /* The --start option */
+ if (run_for_start) {
+ if (start_or_initialize_daemon ())
+ cleanup_and_exit (0);
+ }
/*
- * The whole forking and daemonizing dance starts here.
+ * Always initialize the keyring subsystem. This is a necessary
+ * component that everything else depends on in one way or
+ * another.
*/
- if (!run_foreground) {
- pid = fork ();
-
- /* An intermediate child */
- if (pid == 0) {
- if (run_daemonized) {
- pid = fork ();
-
- /* Still in the intermedate child */
- if (pid != 0) {
- gkr_secure_free (login_password);
-
- /* This process exits, so that the
- * final child will inherit init as parent
- * to avoid zombies
- */
- if (pid == -1) {
- exit (1);
- } else {
- /* This is where we know the pid of the daemon.
- * The initial process will waitpid until we exit,
- * so there is no race */
- print_environment (pid);
- exit (0);
- }
- }
- }
-
- /* final child continues here */
-
- /* The initial process */
- } else {
- gkr_secure_free (login_password);
-
- if (run_daemonized) {
- int status;
-
- /* Initial process, waits for intermediate child */
- if (pid == -1)
- exit (1);
-
- waitpid (pid, &status, 0);
- if (WEXITSTATUS (status) != 0)
- exit (WEXITSTATUS (status));
-
- } else {
- print_environment (pid);
- }
-
- exit (0);
- }
-
- /* The final child ... */
- close_stdinout ();
+ if (!gkr_daemon_io_create_master_socket ())
+ cleanup_and_exit (1);
+ /* The --login option. Delayed initialization */
+ if (run_for_login) {
+ login_password = read_login_password (STDIN);
+ atexit (clear_login_password);
+
+ /* Not a login daemon. Initialize now. */
} else {
- print_environment (getpid ());
+ if (!gkr_daemon_complete_initialization ())
+ cleanup_and_exit (1);
}
+
+ /* The whole forking and daemonizing dance starts here. */
+ fork_and_print_environment();
- /* Daemon process continues here */
-
- /* Send all warning or error messages to syslog */
+ /* Prepare logging a second time, since we may be in a different process */
prepare_logging();
loop = g_main_loop_new (NULL, FALSE);
@@ -683,29 +749,19 @@ main (int argc, char *argv[])
gkr_unix_signal_connect (ctx, SIGHUP, signal_handler, NULL);
gkr_unix_signal_connect (ctx, SIGTERM, signal_handler, NULL);
- env = getenv ("GNOME_KEYRING_LIFETIME_FD");
- if (env && env[0]) {
- fd = atoi (env);
- if (fd != 0) {
- channel = g_io_channel_unix_new (fd);
- g_io_add_watch (channel,
- G_IO_IN | G_IO_HUP,
- lifetime_slave_pipe_io, NULL);
- g_io_channel_unref (channel);
- }
- }
+ /* TODO: Do we still need this? XFCE still seems to use it. */
+ slave_lifetime_to_fd ();
gkr_async_workers_init (loop);
- gkr_daemon_dbus_setup ();
/*
* Unlock the login keyring if we were given a password on STDIN.
* If it does not exist. We create it.
*/
- if (unlock_with_login && login_password) {
+ if (login_password) {
if (!gkr_keyring_login_unlock (login_password))
g_warning ("Failed to unlock login on startup");
- gkr_secure_free (login_password);
+ gkr_secure_strclear (login_password);
}
g_main_loop_run (loop);
diff --git a/daemon/gkr-daemon.h b/daemon/gkr-daemon.h
index ff234233..2641e1b4 100644
--- a/daemon/gkr-daemon.h
+++ b/daemon/gkr-daemon.h
@@ -47,10 +47,12 @@ extern GkrDaemonOperation keyring_ops[];
void gkr_daemon_quit (void);
+gboolean gkr_daemon_complete_initialization (void);
+
gboolean gkr_daemon_io_create_master_socket (void);
+
const gchar* gkr_daemon_io_get_socket_path (void);
-/* Dbus Initialization/Cleanup */
-void gkr_daemon_dbus_setup (void);
+void gkr_daemon_dbus_setup (void);
#endif /* GNOME_KEYRING_DAEMON_H */