2 * Copyright (C) 2010 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
32 static guint signals[LAST_SIGNAL] = { 0 };
38 /* User running this session */
41 /* Environment variables */
44 /* Command to run for this session */
51 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
54 session_new (GKeyFile *config, const char *username, const char *command)
56 return g_object_new (SESSION_TYPE, "config", config, "username", username, "command", command, NULL);
60 session_get_username (Session *session)
62 return session->priv->username;
66 session_get_command (Session *session)
68 return session->priv->command;
72 session_set_env (Session *session, const gchar *name, const gchar *value)
74 g_hash_table_insert (session->priv->env, g_strdup (name), g_strdup (value));
78 session_watch_cb (GPid pid, gint status, gpointer data)
80 Session *session = data;
82 if (WIFEXITED (status))
83 g_debug ("Session exited with return value %d", WEXITSTATUS (status));
84 else if (WIFSIGNALED (status))
85 g_debug ("Session terminated with signal %d", WTERMSIG (status));
87 session->priv->pid = 0;
89 g_signal_emit (session, signals[EXITED], 0);
93 session_fork_cb (gpointer data)
95 struct passwd *user_info = data;
97 if (setgid (user_info->pw_gid) != 0)
99 g_warning ("Failed to set group ID: %s", strerror (errno));
102 // FIXME: Is there a risk of connecting to the process for a user in the given group and accessing memory?
103 if (setuid (user_info->pw_uid) != 0)
105 g_warning ("Failed to set user ID: %s", strerror (errno));
108 if (chdir (user_info->pw_dir) != 0)
109 g_warning ("Failed to change directory: %s", strerror (errno));
112 static gchar **session_get_env (Session *session)
119 env = g_malloc (sizeof (gchar *) * (g_hash_table_size (session->priv->env) + 1));
120 g_hash_table_iter_init (&iter, session->priv->env);
121 while (g_hash_table_iter_next (&iter, &key, &value))
123 // FIXME: Do these need to be freed?
124 env[i] = g_strdup_printf("%s=%s", (gchar *)key, (gchar *)value);
133 session_start (Session *session)
135 struct passwd *user_info;
136 //gint session_stdin, session_stdout, session_stderr;
141 GError *error = NULL;
143 g_return_val_if_fail (session->priv->pid == 0, FALSE);
146 user_info = getpwnam (session->priv->username);
150 g_warning ("Unable to get information on user %s: User does not exist", session->priv->username);
152 g_warning ("Unable to get information on user %s: %s", session->priv->username, strerror (errno));
156 session_set_env (session, "USER", user_info->pw_name);
157 session_set_env (session, "HOME", user_info->pw_dir);
158 session_set_env (session, "SHELL", user_info->pw_shell);
160 env = session_get_env (session);
162 result = g_shell_parse_argv (session->priv->command, &argc, &argv, &error);
164 g_error ("Failed to parse session command line: %s", error->message);
165 g_clear_error (&error);
169 env_string = g_strjoinv (" ", env);
170 g_debug ("Launching greeter: %s %s", env_string, session->priv->command);
173 result = g_spawn_async/*_with_pipes*/ (user_info->pw_dir,
176 G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
177 session_fork_cb, user_info,
179 //&session_stdin, &session_stdout, &session_stderr,
183 g_warning ("Failed to spawn session: %s", error->message);
185 g_child_watch_add (session->priv->pid, session_watch_cb, session);
186 g_clear_error (&error);
188 return session->priv->pid != 0;
192 session_init (Session *session)
194 session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, SESSION_TYPE, SessionPrivate);
195 session->priv->env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
199 session_set_property (GObject *object,
206 self = SESSION (object);
210 self->priv->config = g_value_get_pointer (value);
213 self->priv->username = g_strdup (g_value_get_string (value));
216 self->priv->command = g_strdup (g_value_get_string (value));
219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
225 session_get_property (GObject *object,
232 self = SESSION (object);
236 g_value_set_pointer (value, self->priv->config);
239 g_value_set_string (value, self->priv->username);
242 g_value_set_string (value, self->priv->command);
245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 session_finalize (GObject *object)
255 self = SESSION (object);
257 kill (self->priv->pid, SIGTERM);
258 g_free (self->priv->username);
259 g_hash_table_unref (self->priv->env);
260 g_free (self->priv->command);
264 session_class_init (SessionClass *klass)
266 GObjectClass *object_class = G_OBJECT_CLASS (klass);
268 object_class->set_property = session_set_property;
269 object_class->get_property = session_get_property;
270 object_class->finalize = session_finalize;
272 g_type_class_add_private (klass, sizeof (SessionPrivate));
274 g_object_class_install_property (object_class,
276 g_param_spec_pointer ("config",
279 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
280 g_object_class_install_property (object_class,
282 g_param_spec_string ("username",
286 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
287 g_object_class_install_property (object_class,
289 g_param_spec_string ("command",
291 "Session executable command",
293 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
296 g_signal_new ("exited",
297 G_TYPE_FROM_CLASS (klass),
299 G_STRUCT_OFFSET (SessionClass, exited),
301 g_cclosure_marshal_VOID__VOID,