]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blobdiff - src/session.c
Merge mterrys changes to the PATH mangling for gdmflexiserver
[sojka/lightdm.git] / src / session.c
index af19cd34b370113d0cf144179f2acb2c26680a65..9e9bfad15cf4add1db780991bebd94b8508188fa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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);
@@ -155,17 +216,40 @@ session_start (Session *session, gboolean create_pipe)
     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
@@ -174,24 +258,24 @@ session_finalize (GObject *object)
     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));