• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2006 Lennart Poettering
5 
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <X11/Xlib.h>
29 #include <X11/SM/SMlib.h>
30 
31 #include <pulse/xmalloc.h>
32 #include <pulse/util.h>
33 
34 #include <pulsecore/modargs.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/x11wrap.h>
37 
38 PA_MODULE_AUTHOR("Lennart Poettering");
39 PA_MODULE_DESCRIPTION("X11 session management");
40 PA_MODULE_VERSION(PACKAGE_VERSION);
41 PA_MODULE_LOAD_ONCE(false);
42 PA_MODULE_USAGE("session_manager=<session manager string> display=<X11 display>");
43 
44 static bool ice_in_use = false;
45 
46 static const char* const valid_modargs[] = {
47     "session_manager",
48     "display",
49     "xauthority",
50     NULL
51 };
52 
53 struct userdata {
54     pa_core *core;
55     pa_module *module;
56     pa_client *client;
57     SmcConn connection;
58 
59     pa_x11_wrapper *x11_wrapper;
60     pa_x11_client *x11_client;
61 };
62 
63 typedef struct {
64     IceConn connection;
65     struct userdata *userdata;
66 } ice_io_callback_data;
67 
ice_io_cb_data_new(IceConn connection,struct userdata * userdata)68 static void* ice_io_cb_data_new(IceConn connection, struct userdata *userdata) {
69     ice_io_callback_data *data = pa_xnew(ice_io_callback_data, 1);
70 
71     data->connection = connection;
72     data->userdata = userdata;
73 
74     return data;
75 }
76 
ice_io_cb_data_destroy(pa_mainloop_api * a,pa_io_event * e,void * userdata)77 static void ice_io_cb_data_destroy(pa_mainloop_api*a, pa_io_event *e, void *userdata) {
78     pa_assert(userdata);
79 
80     pa_xfree(userdata);
81 }
82 
x11_kill_cb(pa_x11_wrapper * w,void * userdata)83 static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
84     struct userdata *u = userdata;
85 
86     pa_assert(w);
87     pa_assert(u);
88     pa_assert(u->x11_wrapper == w);
89 
90     pa_log_debug("X11 client kill callback called");
91 
92     if (u->connection) {
93         SmcCloseConnection(u->connection, 0, NULL);
94         u->connection = NULL;
95     }
96 
97     if (u->x11_client) {
98         pa_x11_client_free(u->x11_client);
99         u->x11_client = NULL;
100     }
101 
102     if (u->x11_wrapper) {
103         pa_x11_wrapper_unref(u->x11_wrapper);
104         u->x11_wrapper = NULL;
105     }
106 
107     pa_module_unload_request(u->module, true);
108 }
109 
close_xsmp_connection(struct userdata * userdata)110 static void close_xsmp_connection(struct userdata *userdata) {
111     pa_assert(userdata);
112 
113     if (userdata->connection) {
114         SmcCloseConnection(userdata->connection, 0, NULL);
115         userdata->connection = NULL;
116     }
117 
118     pa_x11_wrapper_kill_deferred(userdata->x11_wrapper);
119 }
120 
die_cb(SmcConn connection,SmPointer client_data)121 static void die_cb(SmcConn connection, SmPointer client_data) {
122     struct userdata *u = client_data;
123 
124     pa_assert(u);
125 
126     pa_log_debug("Got die message from XSMP.");
127 
128     close_xsmp_connection(u);
129 }
130 
save_complete_cb(SmcConn connection,SmPointer client_data)131 static void save_complete_cb(SmcConn connection, SmPointer client_data) {
132 }
133 
shutdown_cancelled_cb(SmcConn connection,SmPointer client_data)134 static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) {
135     SmcSaveYourselfDone(connection, True);
136 }
137 
save_yourself_cb(SmcConn connection,SmPointer client_data,int save_type,Bool _shutdown,int interact_style,Bool fast)138 static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) {
139     SmcSaveYourselfDone(connection, True);
140 }
141 
ice_io_cb(pa_mainloop_api * a,pa_io_event * e,int fd,pa_io_event_flags_t flags,void * userdata)142 static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
143     ice_io_callback_data *io_data = userdata;
144 
145     pa_assert(io_data);
146 
147     if (IceProcessMessages(io_data->connection, NULL, NULL) == IceProcessMessagesIOError) {
148         pa_log_debug("IceProcessMessages: I/O error, closing XSMP.");
149 
150         IceSetShutdownNegotiation(io_data->connection, False);
151 
152         /* SM owns this connection, close via SmcCloseConnection() */
153         close_xsmp_connection(io_data->userdata);
154     }
155 }
156 
new_ice_connection(IceConn connection,IcePointer client_data,Bool opening,IcePointer * watch_data)157 static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) {
158     struct userdata *u = client_data;
159 
160     pa_assert(u);
161 
162     if (opening) {
163         *watch_data = u->core->mainloop->io_new(
164                 u->core->mainloop,
165                 IceConnectionNumber(connection),
166                 PA_IO_EVENT_INPUT,
167                 ice_io_cb,
168                 ice_io_cb_data_new(connection, u));
169 
170         u->core->mainloop->io_set_destroy(*watch_data, ice_io_cb_data_destroy);
171     } else
172         u->core->mainloop->io_free(*watch_data);
173 }
174 
175 static IceIOErrorHandler ice_installed_handler;
176 
177 /* We call any handler installed before (or after) module is loaded but
178    avoid calling the default libICE handler which does an exit() */
179 
ice_io_error_handler(IceConn iceConn)180 static void ice_io_error_handler(IceConn iceConn) {
181     pa_log_warn("ICE I/O error handler called");
182     if (ice_installed_handler)
183       (*ice_installed_handler) (iceConn);
184 }
185 
pa__init(pa_module * m)186 int pa__init(pa_module*m) {
187 
188     pa_modargs *ma = NULL;
189     char t[256], *vendor, *client_id;
190     SmcCallbacks callbacks;
191     SmProp prop_program, prop_user;
192     SmProp *prop_list[2];
193     SmPropValue val_program, val_user;
194     struct userdata *u;
195     const char *e;
196     pa_client_new_data data;
197 
198     pa_assert(m);
199 
200     if (ice_in_use) {
201         pa_log("module-x11-xsmp may not be loaded twice.");
202         return -1;
203     } else {
204         IceIOErrorHandler default_handler;
205 
206         ice_installed_handler = IceSetIOErrorHandler (NULL);
207         default_handler = IceSetIOErrorHandler (ice_io_error_handler);
208 
209         if (ice_installed_handler == default_handler)
210             ice_installed_handler = NULL;
211 
212         IceSetIOErrorHandler(ice_io_error_handler);
213     }
214 
215     m->userdata = u = pa_xnew(struct userdata, 1);
216     u->core = m->core;
217     u->module = m;
218     u->client = NULL;
219     u->connection = NULL;
220     u->x11_wrapper = NULL;
221 
222     IceAddConnectionWatch(new_ice_connection, u);
223     ice_in_use = true;
224 
225     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
226         pa_log("Failed to parse module arguments");
227         goto fail;
228     }
229 
230     if (pa_modargs_get_value(ma, "xauthority", NULL)) {
231         if (setenv("XAUTHORITY", pa_modargs_get_value(ma, "xauthority", NULL), 1)) {
232             pa_log("setenv() for $XAUTHORITY failed");
233             goto fail;
234         }
235     }
236 
237     if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
238         goto fail;
239 
240     u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
241 
242     e = pa_modargs_get_value(ma, "session_manager", NULL);
243 
244     if (!e && !getenv("SESSION_MANAGER")) {
245         pa_log("X11 session manager not running.");
246         goto fail;
247     }
248 
249     memset(&callbacks, 0, sizeof(callbacks));
250     callbacks.die.callback = die_cb;
251     callbacks.die.client_data = u;
252     callbacks.save_yourself.callback = save_yourself_cb;
253     callbacks.save_yourself.client_data = m->core;
254     callbacks.save_complete.callback = save_complete_cb;
255     callbacks.save_complete.client_data = m->core;
256     callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb;
257     callbacks.shutdown_cancelled.client_data = m->core;
258 
259     if (!(u->connection = SmcOpenConnection(
260                   (char*) e, m->core,
261                   SmProtoMajor, SmProtoMinor,
262                   SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
263                   &callbacks, NULL, &client_id,
264                   sizeof(t), t))) {
265 
266         pa_log("Failed to open connection to session manager: %s", t);
267         goto fail;
268     }
269 
270     prop_program.name = (char*) SmProgram;
271     prop_program.type = (char*) SmARRAY8;
272     val_program.value = (char*) PACKAGE_NAME;
273     val_program.length = (int) strlen(val_program.value);
274     prop_program.num_vals = 1;
275     prop_program.vals = &val_program;
276     prop_list[0] = &prop_program;
277 
278     prop_user.name = (char*) SmUserID;
279     prop_user.type = (char*) SmARRAY8;
280     pa_get_user_name(t, sizeof(t));
281     val_user.value = t;
282     val_user.length = (int) strlen(val_user.value);
283     prop_user.num_vals = 1;
284     prop_user.vals = &val_user;
285     prop_list[1] = &prop_user;
286 
287     SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
288 
289     pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
290 
291     pa_client_new_data_init(&data);
292     data.module = m;
293     data.driver = __FILE__;
294     pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id);
295     pa_proplist_sets(data.proplist, "xsmp.vendor", vendor);
296     pa_proplist_sets(data.proplist, "xsmp.client.id", client_id);
297     u->client = pa_client_new(u->core, &data);
298     pa_client_new_data_done(&data);
299 
300     free(vendor);
301     free(client_id);
302 
303     if (!u->client)
304         goto fail;
305 
306     /* Positive exit_idle_time is only useful when we have no session tracking
307      * capability, so we can set it to 0 now that we have detected a session.
308      * The benefit of setting exit_idle_time to 0 is that pulseaudio will exit
309      * immediately when the session ends. That in turn is useful, because some
310      * systems (those that use pam_systemd but don't use systemd for managing
311      * pulseaudio) clean $XDG_RUNTIME_DIR on logout, but fail to terminate all
312      * services that depend on the files in $XDG_RUNTIME_DIR. The directory
313      * contains our sockets, and if the sockets are removed without terminating
314      * pulseaudio, a quick relogin will likely cause trouble, because a new
315      * instance will be spawned while the old instance is still running. */
316     if (u->core->exit_idle_time > 0)
317         pa_core_set_exit_idle_time(u->core, 0);
318 
319     pa_modargs_free(ma);
320 
321     return 0;
322 
323 fail:
324     if (ma)
325         pa_modargs_free(ma);
326 
327     pa__done(m);
328 
329     return -1;
330 }
331 
pa__done(pa_module * m)332 void pa__done(pa_module*m) {
333     struct userdata *u;
334 
335     pa_assert(m);
336 
337     /* set original ICE I/O error handler and forget it */
338     IceSetIOErrorHandler(ice_installed_handler);
339     ice_installed_handler = NULL;
340 
341     if ((u = m->userdata)) {
342 
343         if (u->connection)
344             SmcCloseConnection(u->connection, 0, NULL);
345 
346         if (u->client)
347             pa_client_free(u->client);
348 
349         if (u->x11_client)
350             pa_x11_client_free(u->x11_client);
351 
352         if (u->x11_wrapper)
353             pa_x11_wrapper_unref(u->x11_wrapper);
354     }
355 
356     if (ice_in_use) {
357         IceRemoveConnectionWatch(new_ice_connection, u);
358         ice_in_use = false;
359     }
360 
361     if (u)
362         pa_xfree(u);
363 }
364