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
26 static guint signals[LAST_SIGNAL] = { 0 };
30 /* User running this session */
33 /* Environment variables */
36 /* Command to run for this session */
43 XAuthorization *authorization;
44 gchar *authorization_path;
45 GFile *authorization_file;
48 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
51 session_new (const char *username, const char *command)
53 Session *self = g_object_new (SESSION_TYPE, NULL);
55 self->priv->username = g_strdup (username);
56 self->priv->command = g_strdup (command);
62 session_get_username (Session *session)
64 return session->priv->username;
68 session_get_command (Session *session)
70 return session->priv->command;
74 session_set_env (Session *session, const gchar *name, const gchar *value)
76 g_hash_table_insert (session->priv->env, g_strdup (name), g_strdup (value));
80 session_set_authorization (Session *session, XAuthorization *authorization, const gchar *path)
82 session->priv->authorization = g_object_ref (authorization);
83 session->priv->authorization_path = g_strdup (path);
86 XAuthorization *session_get_authorization (Session *session)
88 return session->priv->authorization;
92 session_watch_cb (GPid pid, gint status, gpointer data)
94 Session *session = data;
96 session->priv->pid = 0;
98 if (WIFEXITED (status))
100 g_debug ("Session exited with return value %d", WEXITSTATUS (status));
101 g_signal_emit (session, signals[EXITED], 0, WEXITSTATUS (status));
103 else if (WIFSIGNALED (status))
105 g_debug ("Session terminated with signal %d", WTERMSIG (status));
106 g_signal_emit (session, signals[KILLED], 0, WTERMSIG (status));
111 session_fork_cb (gpointer data)
113 struct passwd *user_info = data;
118 if (setgid (user_info->pw_gid) != 0)
120 g_warning ("Failed to set group ID: %s", strerror (errno));
123 // FIXME: Is there a risk of connecting to the process for a user in the given group and accessing memory?
124 if (setuid (user_info->pw_uid) != 0)
126 g_warning ("Failed to set user ID: %s", strerror (errno));
129 if (chdir (user_info->pw_dir) != 0)
130 g_warning ("Failed to change directory: %s", strerror (errno));
133 static gchar **session_get_env (Session *session)
140 env = g_malloc (sizeof (gchar *) * (g_hash_table_size (session->priv->env) + 1));
141 g_hash_table_iter_init (&iter, session->priv->env);
142 while (g_hash_table_iter_next (&iter, &key, &value))
144 // FIXME: Do these need to be freed?
145 env[i] = g_strdup_printf("%s=%s", (gchar *)key, (gchar *)value);
154 session_start (Session *session)
156 struct passwd *user_info = NULL;
157 //gint session_stdin, session_stdout, session_stderr;
161 const gchar *working_dir = NULL;
164 GError *error = NULL;
166 g_return_val_if_fail (session->priv->pid == 0, FALSE);
168 if (session->priv->username)
171 user_info = getpwnam (session->priv->username);
175 g_warning ("Unable to get information on user %s: User does not exist", session->priv->username);
177 g_warning ("Unable to get information on user %s: %s", session->priv->username, strerror (errno));
181 username = session->priv->username;
182 working_dir = user_info->pw_dir;
183 session_set_env (session, "USER", user_info->pw_name);
184 session_set_env (session, "HOME", user_info->pw_dir);
185 session_set_env (session, "SHELL", user_info->pw_shell);
189 username = getenv ("USER");
190 session_set_env (session, "USER", getenv ("USER"));
191 session_set_env (session, "HOME", getenv ("HOME"));
192 session_set_env (session, "SHELL", getenv ("SHELL"));
195 if (session->priv->authorization)
197 session->priv->authorization_file = xauth_write (session->priv->authorization, username, session->priv->authorization_path, &error);
198 if (session->priv->authorization_file)
199 session_set_env (session, "XAUTHORITY", session->priv->authorization_path);
201 g_warning ("Failed to write authorization: %s", error->message);
202 g_clear_error (&error);
205 env = session_get_env (session);
207 result = g_shell_parse_argv (session->priv->command, &argc, &argv, &error);
209 g_warning ("Failed to parse session command line: %s", error->message);
210 g_clear_error (&error);
214 env_string = g_strjoinv (" ", env);
215 g_debug ("Launching session: %s %s", env_string, session->priv->command);
218 result = g_spawn_async/*_with_pipes*/ (working_dir,
221 G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
222 session_fork_cb, user_info,
224 //&session_stdin, &session_stdout, &session_stderr,
228 g_warning ("Failed to spawn session: %s", error->message);
230 g_child_watch_add (session->priv->pid, session_watch_cb, session);
231 g_clear_error (&error);
233 return session->priv->pid != 0;
237 session_init (Session *session)
239 session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, SESSION_TYPE, SessionPrivate);
240 session->priv->env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
244 session_finalize (GObject *object)
248 self = SESSION (object);
250 kill (self->priv->pid, SIGTERM);
251 g_free (self->priv->username);
252 g_hash_table_unref (self->priv->env);
253 g_free (self->priv->command);
254 if (self->priv->authorization)
255 g_object_unref (self->priv->authorization);
256 g_free (self->priv->authorization_path);
257 if (self->priv->authorization_file)
259 g_file_delete (self->priv->authorization_file, NULL, NULL);
260 g_object_unref (self->priv->authorization_file);
265 session_class_init (SessionClass *klass)
267 GObjectClass *object_class = G_OBJECT_CLASS (klass);
269 object_class->finalize = session_finalize;
271 g_type_class_add_private (klass, sizeof (SessionPrivate));
274 g_signal_new ("exited",
275 G_TYPE_FROM_CLASS (klass),
277 G_STRUCT_OFFSET (SessionClass, exited),
279 g_cclosure_marshal_VOID__INT,
280 G_TYPE_NONE, 1, G_TYPE_INT);
282 g_signal_new ("killed",
283 G_TYPE_FROM_CLASS (klass),
285 G_STRUCT_OFFSET (SessionClass, killed),
287 g_cclosure_marshal_VOID__INT,
288 G_TYPE_NONE, 1, G_TYPE_INT);