]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/session.c
Clean up on exit
[sojka/lightdm.git] / src / session.c
1 /*
2  * Copyright (C) 2010 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  * 
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
9  * license.
10  */
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/wait.h>
15 #include <pwd.h>
16 #include <errno.h>
17 #include <string.h>
18
19 #include "session.h"
20
21 enum {
22     PROP_0,
23     PROP_CONFIG,
24     PROP_USERNAME,
25     PROP_COMMAND
26 };
27
28 enum {
29     EXITED,
30     LAST_SIGNAL
31 };
32 static guint signals[LAST_SIGNAL] = { 0 };
33
34 struct SessionPrivate
35 {
36     GKeyFile *config;
37
38     /* User running this session */
39     gchar *username;
40
41     /* Environment variables */
42     GHashTable *env;
43
44     /* Command to run for this session */
45     gchar *command;
46
47     /* Session process */
48     GPid pid;
49 };
50
51 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
52
53 Session *
54 session_new (GKeyFile *config, const char *username, const char *command)
55 {
56     return g_object_new (SESSION_TYPE, "config", config, "username", username, "command", command, NULL);
57 }
58
59 const gchar *
60 session_get_username (Session *session)
61 {
62     return session->priv->username;
63 }
64
65 const gchar *
66 session_get_command (Session *session)
67 {
68     return session->priv->command;
69 }
70
71 void
72 session_set_env (Session *session, const gchar *name, const gchar *value)
73 {
74     g_hash_table_insert (session->priv->env, g_strdup (name), g_strdup (value));
75 }
76
77 static void
78 session_watch_cb (GPid pid, gint status, gpointer data)
79 {
80     Session *session = data;
81
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));
86
87     session->priv->pid = 0;
88
89     g_signal_emit (session, signals[EXITED], 0);
90 }
91
92 static void
93 session_fork_cb (gpointer data)
94 {
95     struct passwd *user_info = data;
96   
97     if (setgid (user_info->pw_gid) != 0)
98     {
99         g_warning ("Failed to set group ID: %s", strerror (errno));
100         _exit(1);
101     }
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)
104     {
105         g_warning ("Failed to set user ID: %s", strerror (errno));
106         _exit(1);
107     }
108     if (chdir (user_info->pw_dir) != 0)
109         g_warning ("Failed to change directory: %s", strerror (errno));
110 }
111
112 static gchar **session_get_env (Session *session)
113 {
114     gchar **env;
115     gpointer key, value;
116     GHashTableIter iter;
117     gint i = 0;
118
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))
122     {
123         // FIXME: Do these need to be freed?
124         env[i] = g_strdup_printf("%s=%s", (gchar *)key, (gchar *)value);
125         i++;
126     }
127     env[i] = NULL;
128
129     return env;
130 }
131
132 gboolean
133 session_start (Session *session)
134 {
135     struct passwd *user_info;
136     //gint session_stdin, session_stdout, session_stderr;
137     gboolean result;
138     gint argc;
139     gchar **argv, **env;
140     gchar *env_string;
141     GError *error = NULL;
142
143     g_return_val_if_fail (session->priv->pid == 0, FALSE);
144
145     errno = 0;
146     user_info = getpwnam (session->priv->username);
147     if (!user_info)
148     {
149         if (errno == 0)
150             g_warning ("Unable to get information on user %s: User does not exist", session->priv->username);
151         else
152             g_warning ("Unable to get information on user %s: %s", session->priv->username, strerror (errno));
153         return FALSE;
154     }
155   
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);
159
160     env = session_get_env (session);
161
162     result = g_shell_parse_argv (session->priv->command, &argc, &argv, &error);
163     if (!result)
164         g_error ("Failed to parse session command line: %s", error->message);
165     g_clear_error (&error);
166     if (!result)
167         return FALSE;
168
169     env_string = g_strjoinv (" ", env);
170     g_debug ("Launching greeter: %s %s", env_string, session->priv->command);
171     g_free (env_string);
172
173     result = g_spawn_async/*_with_pipes*/ (user_info->pw_dir,
174                                        argv,
175                                        env,
176                                        G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
177                                        session_fork_cb, user_info,
178                                        &session->priv->pid,
179                                        //&session_stdin, &session_stdout, &session_stderr,
180                                        &error);
181
182     if (!result)
183         g_warning ("Failed to spawn session: %s", error->message);
184     else
185         g_child_watch_add (session->priv->pid, session_watch_cb, session);
186     g_clear_error (&error);
187
188     return session->priv->pid != 0;
189 }
190
191 static void
192 session_init (Session *session)
193 {
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);
196 }
197
198 static void
199 session_set_property (GObject      *object,
200                       guint         prop_id,
201                       const GValue *value,
202                       GParamSpec   *pspec)
203 {
204     Session *self;
205
206     self = SESSION (object);
207
208     switch (prop_id) {
209     case PROP_CONFIG:
210         self->priv->config = g_value_get_pointer (value);
211         break;
212     case PROP_USERNAME:
213         self->priv->username = g_strdup (g_value_get_string (value));
214         break;
215     case PROP_COMMAND:
216         self->priv->command = g_strdup (g_value_get_string (value));
217         break;
218     default:
219         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220         break;
221     }
222 }
223
224 static void
225 session_get_property (GObject    *object,
226                       guint       prop_id,
227                       GValue     *value,
228                       GParamSpec *pspec)
229 {
230     Session *self;
231
232     self = SESSION (object);
233
234     switch (prop_id) {
235     case PROP_CONFIG:
236         g_value_set_pointer (value, self->priv->config);
237         break;
238     case PROP_USERNAME:
239         g_value_set_string (value, self->priv->username);
240         break;
241     case PROP_COMMAND:
242         g_value_set_string (value, self->priv->command);
243         break;
244     default:
245         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246         break;
247     }
248 }
249
250 static void
251 session_finalize (GObject *object)
252 {
253     Session *self;
254
255     self = SESSION (object);
256     if (self->priv->pid)
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);
261 }
262
263 static void
264 session_class_init (SessionClass *klass)
265 {
266     GObjectClass *object_class = G_OBJECT_CLASS (klass);
267
268     object_class->set_property = session_set_property;
269     object_class->get_property = session_get_property;
270     object_class->finalize = session_finalize;
271
272     g_type_class_add_private (klass, sizeof (SessionPrivate));
273
274     g_object_class_install_property (object_class,
275                                      PROP_CONFIG,
276                                      g_param_spec_pointer ("config",
277                                                            "config",
278                                                            "Configuration",
279                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
280     g_object_class_install_property (object_class,
281                                      PROP_USERNAME,
282                                      g_param_spec_string ("username",
283                                                           "username",
284                                                           "Session user",
285                                                           NULL,
286                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
287     g_object_class_install_property (object_class,
288                                      PROP_COMMAND,
289                                      g_param_spec_string ("command",
290                                                           "command",
291                                                           "Session executable command",
292                                                           NULL,
293                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
294
295     signals[EXITED] =
296         g_signal_new ("exited",
297                       G_TYPE_FROM_CLASS (klass),
298                       G_SIGNAL_RUN_LAST,
299                       G_STRUCT_OFFSET (SessionClass, exited),
300                       NULL, NULL,
301                       g_cclosure_marshal_VOID__VOID,
302                       G_TYPE_NONE, 0);
303 }