~jamesodhunt/upstart/foo

« back to all changes in this revision

Viewing changes to init/session.c

Merge of lp:~jamesodhunt/upstart/upstream-session-support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* upstart
 
2
 *
 
3
 * session.c - session segregation
 
4
 *
 
5
 * Copyright © 2010,2011 Canonical Ltd.
 
6
 * Author: Scott James Remnant <scott@netsplit.com>.
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License version 2, as
 
10
 * published by the Free Software Foundation.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 * GNU General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU General Public License along
 
18
 * with this program; if not, write to the Free Software Foundation, Inc.,
 
19
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
# include <config.h>
 
24
#endif /* HAVE_CONFIG_H */
 
25
 
 
26
 
 
27
#include <dbus/dbus.h>
 
28
 
 
29
#include <sys/types.h>
 
30
 
 
31
#include <pwd.h>
 
32
#include <errno.h>
 
33
#include <limits.h>
 
34
#include <string.h>
 
35
#include <unistd.h>
 
36
 
 
37
#include <nih/macros.h>
 
38
#include <nih/alloc.h>
 
39
#include <nih/list.h>
 
40
#include <nih/string.h>
 
41
#include <nih/logging.h>
 
42
#include <nih/error.h>
 
43
 
 
44
#include <nih-dbus/dbus_message.h>
 
45
 
 
46
#include "session.h"
 
47
#include "conf.h"
 
48
#include "paths.h"
 
49
 
 
50
 
 
51
/**
 
52
 * sessions:
 
53
 *
 
54
 * This list holds the list of known sessions; each item is a Session
 
55
 * structure.
 
56
 **/
 
57
NihList *sessions = NULL;
 
58
 
 
59
/**
 
60
 * disable_sessions:
 
61
 *
 
62
 * If TRUE, disable user and chroot sessions, resulting in a
 
63
 * "traditional" (pre-session support) system.
 
64
 **/
 
65
int disable_sessions = FALSE;
 
66
 
 
67
 
 
68
/* Prototypes for static functions */
 
69
static void session_create_conf_source (Session *sesson);
 
70
 
 
71
 
 
72
/**
 
73
 * session_init:
 
74
 *
 
75
 * Initialise the sessions list.
 
76
 **/
 
77
void
 
78
session_init (void)
 
79
{
 
80
        if (! sessions)
 
81
                sessions = NIH_MUST (nih_list_new (NULL));
 
82
}
 
83
 
 
84
 
 
85
Session *
 
86
session_new (const void *parent,
 
87
             const char *chroot,
 
88
             uid_t       user)
 
89
{
 
90
        Session *session;
 
91
 
 
92
        nih_assert ((chroot != NULL) || (user != 0));
 
93
 
 
94
        session_init ();
 
95
 
 
96
        session = nih_new (parent, Session);
 
97
        if (! session)
 
98
                return NULL;
 
99
 
 
100
        nih_list_init (&session->entry);
 
101
 
 
102
        if (chroot) {
 
103
                session->chroot = nih_strdup (session, chroot);
 
104
                if (! session->chroot) {
 
105
                        nih_free (session);
 
106
                        return NULL;
 
107
                }
 
108
        } else {
 
109
                session->chroot = NULL;
 
110
        }
 
111
 
 
112
        session->user = user;
 
113
 
 
114
        session->conf_path = NULL;
 
115
 
 
116
        nih_alloc_set_destructor (session, nih_list_destroy);
 
117
 
 
118
        nih_list_add (sessions, &session->entry);
 
119
 
 
120
        return session;
 
121
}
 
122
 
 
123
Session *
 
124
session_from_dbus (const void     *parent,
 
125
                   NihDBusMessage *message)
 
126
{
 
127
        const char   *sender;
 
128
        DBusError     dbus_error;
 
129
        unsigned long unix_user;
 
130
        unsigned long unix_process_id;
 
131
        char          root[PATH_MAX];
 
132
        Session      *session;
 
133
 
 
134
        nih_assert (message != NULL);
 
135
 
 
136
        /* Handle explicit command-line request and alternative request
 
137
         * method (primarily for test framework) to disable session support.
 
138
         */
 
139
        if (disable_sessions || getenv ("UPSTART_NO_SESSIONS"))
 
140
                return NULL;
 
141
 
 
142
        session_init ();
 
143
 
 
144
        /* Ask D-Bus nicely for the origin uid and/or pid of the caller;
 
145
         * sadly we can't ask the bus daemon for the origin pid, so that
 
146
         * one will just have to stay user-session only.
 
147
         */
 
148
        dbus_error_init (&dbus_error);
 
149
 
 
150
        sender = dbus_message_get_sender (message->message);
 
151
        if (sender) {
 
152
                unix_user = dbus_bus_get_unix_user (message->connection, sender,
 
153
                                                    &dbus_error);
 
154
                if (unix_user == (unsigned long)-1) {
 
155
                        dbus_error_free (&dbus_error);
 
156
                        unix_user = 0;
 
157
                }
 
158
 
 
159
                unix_process_id = 0;
 
160
 
 
161
        } else {
 
162
                if (! dbus_connection_get_unix_user (message->connection,
 
163
                                                     &unix_user))
 
164
                        unix_process_id = 0;
 
165
 
 
166
                if (! dbus_connection_get_unix_process_id (message->connection,
 
167
                                                           &unix_process_id))
 
168
                        unix_process_id = 0;
 
169
        }
 
170
 
 
171
        /* If we retrieved a process id, look up the root path for it;
 
172
         * if it's just '/' don't worry so much about it.
 
173
         */
 
174
        if (unix_process_id) {
 
175
                nih_local char *symlink = NULL;
 
176
                ssize_t len;
 
177
 
 
178
                symlink = NIH_MUST (nih_sprintf (NULL, "/proc/%lu/root",
 
179
                                                 unix_process_id));
 
180
                len = readlink (symlink, root, sizeof root);
 
181
                if (len < 0)
 
182
                        return NULL;
 
183
 
 
184
                root[len] = '\0';
 
185
 
 
186
                if (! strcmp (root, "/")) {
 
187
                        unix_process_id = 0;
 
188
                        if (! unix_user)
 
189
                                return NULL;
 
190
                }
 
191
 
 
192
        } else if (! unix_user) {
 
193
                /* No process id or user id found, return the NULL session */
 
194
                return NULL;
 
195
 
 
196
        }
 
197
 
 
198
        /* Now find in the existing Sessions list */
 
199
        NIH_LIST_FOREACH (sessions, iter) {
 
200
                Session *session = (Session *)iter;
 
201
 
 
202
                if (unix_process_id) {
 
203
                        if (! session->chroot)
 
204
                                continue;
 
205
 
 
206
                        /* ignore sessions relating to other chroots */
 
207
                        if (strcmp (session->chroot, root))
 
208
                                continue;
 
209
                }
 
210
 
 
211
                /* ignore sessions relating to other users */
 
212
                if (unix_user != session->user)
 
213
                        continue;
 
214
 
 
215
                if (! session->conf_path)
 
216
                        session_create_conf_source (session);
 
217
 
 
218
                return session;
 
219
        }
 
220
 
 
221
 
 
222
        /* Didn't find one, make a new one */
 
223
        session = NIH_MUST (session_new (parent, unix_process_id ? root : NULL,
 
224
                                         unix_user));
 
225
        session_create_conf_source (session);
 
226
 
 
227
        return session;
 
228
}
 
229
 
 
230
static void
 
231
session_create_conf_source (Session *session)
 
232
{
 
233
        ConfSource *source;
 
234
 
 
235
        nih_assert (session != NULL);
 
236
        nih_assert (session->conf_path == NULL);
 
237
 
 
238
        if (session->chroot)
 
239
                session->conf_path = NIH_MUST (nih_strdup (NULL, session->chroot));
 
240
        if (session->user) {
 
241
                struct passwd *pwd;
 
242
 
 
243
                pwd = getpwuid (session->user);
 
244
                if (! pwd) {
 
245
                        nih_error ("%d: %s: %s", session->user,
 
246
                                   _("Unable to lookup home directory"),
 
247
                                   strerror (errno));
 
248
 
 
249
                        nih_free (session->conf_path);
 
250
                        session->conf_path = NULL;
 
251
                        return;
 
252
                }
 
253
 
 
254
                NIH_MUST (nih_strcat_sprintf (&session->conf_path, NULL, "%s/%s",
 
255
                                              pwd->pw_dir, USERCONFDIR));
 
256
        } else {
 
257
                NIH_MUST (nih_strcat (&session->conf_path, NULL, CONFDIR));
 
258
        }
 
259
 
 
260
        source = NIH_MUST (conf_source_new (session, session->conf_path,
 
261
                                            CONF_JOB_DIR));
 
262
        source->session = session;
 
263
 
 
264
        if (conf_source_reload (source) < 0) {
 
265
                NihError *err;
 
266
 
 
267
                err = nih_error_get ();
 
268
                if (err->number != ENOENT)
 
269
                        nih_error ("%s: %s: %s", source->path,
 
270
                                   _("Unable to load configuration"),
 
271
                                   err->message);
 
272
                nih_free (err);
 
273
 
 
274
                nih_free (source);
 
275
                nih_free (session->conf_path);
 
276
                session->conf_path = NULL;
 
277
                return;
 
278
        }
 
279
}