/*
- * Copyright (C) 2010 Robert Ancell.
+ * Copyright (C) 2010-2011 Robert Ancell.
* Author: Robert Ancell <robert.ancell@canonical.com>
*
* This program is free software: you can redistribute it and/or modify it under
*/
#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
-#include <pwd.h>
-#include <errno.h>
-#include <string.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include <grp.h>
struct SessionPrivate
{
- /* User running this session */
- gchar *username;
+ /* Authentication for this session */
+ PAMSession *authentication;
- /* Path of file to log to */
- gchar *log_file;
-
- /* Environment variables */
- GHashTable *env;
-
/* Command to run for this session */
gchar *command;
- /* X authorization */
- XAuthorization *authorization;
- gchar *authorization_path;
- GFile *authorization_file;
+ /* Cookie for the session */
+ gchar *cookie;
+
+ /* TRUE if this is a greeter session */
+ gboolean is_greeter;
};
-G_DEFINE_TYPE (Session, session, CHILD_PROCESS_TYPE);
+G_DEFINE_TYPE (Session, session, PROCESS_TYPE);
+
+void
+session_set_authentication (Session *session, PAMSession *authentication)
+{
+ g_return_if_fail (session != NULL);
+ session->priv->authentication = g_object_ref (authentication);
+}
+
+PAMSession *
+session_get_authentication (Session *session)
+{
+ g_return_val_if_fail (session != NULL, NULL);
+ return session->priv->authentication;
+}
-Session *
-session_new ()
+User *
+session_get_user (Session *session)
{
- return g_object_new (SESSION_TYPE, NULL);
+ g_return_val_if_fail (session != NULL, NULL);
+ return pam_session_get_user (session->priv->authentication);
}
void
-session_set_username (Session *session, const gchar *username)
+session_set_is_greeter (Session *session, gboolean is_greeter)
{
- session->priv->username = g_strdup (username);
+ g_return_if_fail (session != NULL);
+ session->priv->is_greeter = is_greeter;
}
-const gchar *
-session_get_username (Session *session)
+gboolean
+session_get_is_greeter (Session *session)
{
- return session->priv->username;
+ g_return_val_if_fail (session != NULL, FALSE);
+ return session->priv->is_greeter;
}
void
session_set_command (Session *session, const gchar *command)
{
+ g_return_if_fail (session != NULL);
+
+ g_free (session->priv->command);
session->priv->command = g_strdup (command);
}
const gchar *
session_get_command (Session *session)
{
+ g_return_val_if_fail (session != NULL, NULL);
return session->priv->command;
}
void
-session_set_authorization (Session *session, XAuthorization *authorization, const gchar *path)
+session_set_cookie (Session *session, const gchar *cookie)
{
- session->priv->authorization = g_object_ref (authorization);
- session->priv->authorization_path = g_strdup (path);
+ g_return_if_fail (session != NULL);
+
+ g_free (session->priv->cookie);
+ session->priv->cookie = g_strdup (cookie);
}
-XAuthorization *session_get_authorization (Session *session)
+const gchar *
+session_get_cookie (Session *session)
{
- return session->priv->authorization;
+ g_return_val_if_fail (session != NULL, NULL);
+ return session->priv->cookie;
}
-gboolean
-session_start (Session *session, gboolean create_pipe)
+static gchar *
+get_absolute_command (const gchar *command)
{
- //gint session_stdin, session_stdout, session_stderr;
- gboolean result;
- struct passwd *user_info;
- gchar *username, *working_dir;
- GError *error = NULL;
-
- //g_return_val_if_fail (session->priv->pid == 0, FALSE);
- g_return_val_if_fail (session->priv->command != NULL, FALSE);
+ gchar **tokens;
+ gchar *absolute_binary, *absolute_command = NULL;
- errno = 0;
- if (session->priv->username)
+ tokens = g_strsplit (command, " ", 2);
+
+ absolute_binary = g_find_program_in_path (tokens[0]);
+ if (absolute_binary)
{
- user_info = getpwnam (session->priv->username);
- if (!user_info)
- {
- if (errno == 0)
- g_warning ("Unable to get information on user %s: User does not exist", session->priv->username);
- else
- g_warning ("Unable to get information on user %s: %s", session->priv->username, strerror (errno));
- return FALSE;
- }
+ if (tokens[1])
+ absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
+ else
+ absolute_command = g_strdup (absolute_binary);
}
- else
+
+ g_strfreev (tokens);
+
+ return absolute_command;
+}
+
+static void
+set_env_from_authentication (Session *session, PAMSession *authentication)
+{
+ gchar **pam_env;
+
+ pam_env = pam_session_get_envlist (authentication);
+ if (pam_env)
{
- user_info = getpwuid (getuid ());
- if (!user_info)
+ gchar *env_string;
+ int i;
+
+ env_string = g_strjoinv (" ", pam_env);
+ g_debug ("PAM returns environment '%s'", env_string);
+ g_free (env_string);
+
+ for (i = 0; pam_env[i]; i++)
{
- g_warning ("Unable to determine current username: %s", strerror (errno));
- return FALSE;;
+ gchar **pam_env_vars = g_strsplit (pam_env[i], "=", 2);
+ if (pam_env_vars && pam_env_vars[0] && pam_env_vars[1])
+ process_set_env (PROCESS (session), pam_env_vars[0], pam_env_vars[1]);
+ else
+ g_warning ("Can't parse PAM environment variable %s", pam_env[i]);
+ g_strfreev (pam_env_vars);
}
+ g_strfreev (pam_env);
}
+}
+
+static gboolean
+session_real_start (Session *session)
+{
+ //gint session_stdin, session_stdout, session_stderr;
+ gboolean result;
+ User *user;
+ gchar *absolute_command;
+ const gchar *orig_path;
+ GError *error = NULL;
- username = g_strdup (user_info->pw_name);
- working_dir = g_strdup (user_info->pw_dir);
- child_process_set_env (CHILD_PROCESS (session), "USER", user_info->pw_name);
- child_process_set_env (CHILD_PROCESS (session), "USERNAME", user_info->pw_name); // FIXME: Is this required?
- child_process_set_env (CHILD_PROCESS (session), "HOME", user_info->pw_dir);
- child_process_set_env (CHILD_PROCESS (session), "SHELL", user_info->pw_shell);
+ g_return_val_if_fail (session->priv->authentication != NULL, FALSE);
+ g_return_val_if_fail (session->priv->command != NULL, FALSE);
- if (session->priv->authorization)
+ absolute_command = get_absolute_command (session->priv->command);
+ if (!absolute_command)
{
- session->priv->authorization_file = xauth_write (session->priv->authorization, username, session->priv->authorization_path, &error);
- if (session->priv->authorization_file)
- child_process_set_env (CHILD_PROCESS (session), "XAUTHORITY", session->priv->authorization_path);
- else
- g_warning ("Failed to write authorization: %s", error->message);
- g_clear_error (&error);
+ g_debug ("Can't launch session %s, not found in path", session->priv->command);
+ return FALSE;
}
- g_free (username);
+
+ pam_session_open (session->priv->authentication);
g_debug ("Launching session");
- result = child_process_start (CHILD_PROCESS (session),
- session->priv->username,
- working_dir,
- session->priv->command,
- create_pipe,
- &error);
- g_free (working_dir);
+ user = pam_session_get_user (session->priv->authentication);
+ process_set_env (PROCESS (session), "PATH", "/usr/local/bin:/usr/bin:/bin");
+ process_set_env (PROCESS (session), "USER", user_get_name (user));
+ process_set_env (PROCESS (session), "USERNAME", user_get_name (user)); // FIXME: Is this required?
+ process_set_env (PROCESS (session), "HOME", user_get_home_directory (user));
+ process_set_env (PROCESS (session), "SHELL", user_get_shell (user));
+ set_env_from_authentication (session, session->priv->authentication);
+
+ /* Insert our own utility directory to PATH
+ * This is to provide gdmflexiserver which provides backwards compatibility with GDM.
+ * Must be done after set_env_from_authentication because that often sets PATH.
+ * This can be removed when this is no longer required.
+ */
+ orig_path = process_get_env (PROCESS (session), "PATH");
+ if (orig_path)
+ {
+ gchar *path = g_strdup_printf ("%s:%s", PKGLIBEXEC_DIR, orig_path);
+ process_set_env (PROCESS (session), "PATH", path);
+ g_free (path);
+ }
+
+ if (session->priv->cookie)
+ process_set_env (PROCESS (session), "XDG_SESSION_COOKIE", session->priv->cookie);
+
+ result = process_start (PROCESS (session),
+ user,
+ user_get_home_directory (user),
+ absolute_command,
+ &error);
+ g_free (absolute_command);
if (!result)
g_warning ("Failed to spawn session: %s", error->message);
return result;
}
+gboolean
+session_start (Session *session)
+{
+ g_return_val_if_fail (session != NULL, FALSE);
+ return SESSION_GET_CLASS (session)->start (session);
+}
+
+static void
+session_real_stop (Session *session)
+{
+ process_signal (PROCESS (session), SIGTERM);
+}
+
void
session_stop (Session *session)
{
- child_process_signal (CHILD_PROCESS (session), SIGTERM);
+ g_return_if_fail (session != NULL);
+ SESSION_GET_CLASS (session)->stop (session);
+}
+
+static void
+session_stopped (Process *process)
+{
+ Session *session = SESSION (process);
+
+ pam_session_close (session->priv->authentication);
+
+ PROCESS_CLASS (session_parent_class)->stopped (process);
}
static void
session_init (Session *session)
{
session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, SESSION_TYPE, SessionPrivate);
- session->priv->env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
}
static void
Session *self;
self = SESSION (object);
- g_free (self->priv->username);
- g_hash_table_unref (self->priv->env);
+
+ if (self->priv->authentication)
+ g_object_unref (self->priv->authentication);
g_free (self->priv->command);
- if (self->priv->authorization)
- g_object_unref (self->priv->authorization);
- g_free (self->priv->authorization_path);
- if (self->priv->authorization_file)
- {
- g_file_delete (self->priv->authorization_file, NULL, NULL);
- g_object_unref (self->priv->authorization_file);
- }
+ g_free (self->priv->cookie);
+
+ G_OBJECT_CLASS (session_parent_class)->finalize (object);
}
static void
session_class_init (SessionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ProcessClass *process_class = PROCESS_CLASS (klass);
+ klass->start = session_real_start;
+ klass->stop = session_real_stop;
+ process_class->stopped = session_stopped;
object_class->finalize = session_finalize;
g_type_class_add_private (klass, sizeof (SessionPrivate));