• 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     pa_x11_wrapper *x11;
59 };
60 
die_cb(SmcConn connection,SmPointer client_data)61 static void die_cb(SmcConn connection, SmPointer client_data) {
62     struct userdata *u = client_data;
63     pa_assert(u);
64 
65     pa_log_debug("Got die message from XSMP.");
66 
67     pa_x11_wrapper_kill(u->x11);
68 
69     pa_x11_wrapper_unref(u->x11);
70     u->x11 = NULL;
71 
72     pa_module_unload_request(u->module, true);
73 }
74 
save_complete_cb(SmcConn connection,SmPointer client_data)75 static void save_complete_cb(SmcConn connection, SmPointer client_data) {
76 }
77 
shutdown_cancelled_cb(SmcConn connection,SmPointer client_data)78 static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) {
79     SmcSaveYourselfDone(connection, True);
80 }
81 
save_yourself_cb(SmcConn connection,SmPointer client_data,int save_type,Bool _shutdown,int interact_style,Bool fast)82 static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) {
83     SmcSaveYourselfDone(connection, True);
84 }
85 
ice_io_cb(pa_mainloop_api * a,pa_io_event * e,int fd,pa_io_event_flags_t flags,void * userdata)86 static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
87     IceConn connection = userdata;
88 
89     if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) {
90         IceSetShutdownNegotiation(connection, False);
91         IceCloseConnection(connection);
92     }
93 }
94 
new_ice_connection(IceConn connection,IcePointer client_data,Bool opening,IcePointer * watch_data)95 static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) {
96     pa_core *c = client_data;
97 
98     if (opening)
99         *watch_data = c->mainloop->io_new(
100                 c->mainloop,
101                 IceConnectionNumber(connection),
102                 PA_IO_EVENT_INPUT,
103                 ice_io_cb,
104                 connection);
105     else
106         c->mainloop->io_free(*watch_data);
107 }
108 
pa__init(pa_module * m)109 int pa__init(pa_module*m) {
110 
111     pa_modargs *ma = NULL;
112     char t[256], *vendor, *client_id;
113     SmcCallbacks callbacks;
114     SmProp prop_program, prop_user;
115     SmProp *prop_list[2];
116     SmPropValue val_program, val_user;
117     struct userdata *u;
118     const char *e;
119     pa_client_new_data data;
120 
121     pa_assert(m);
122 
123     if (ice_in_use) {
124         pa_log("module-x11-xsmp may not be loaded twice.");
125         return -1;
126     }
127 
128     IceAddConnectionWatch(new_ice_connection, m->core);
129     ice_in_use = true;
130 
131     m->userdata = u = pa_xnew(struct userdata, 1);
132     u->core = m->core;
133     u->module = m;
134     u->client = NULL;
135     u->connection = NULL;
136     u->x11 = NULL;
137 
138     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
139         pa_log("Failed to parse module arguments");
140         goto fail;
141     }
142 
143     if (pa_modargs_get_value(ma, "xauthority", NULL)) {
144         if (setenv("XAUTHORITY", pa_modargs_get_value(ma, "xauthority", NULL), 1)) {
145             pa_log("setenv() for $XAUTHORITY failed");
146             goto fail;
147         }
148     }
149 
150     if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
151         goto fail;
152 
153     e = pa_modargs_get_value(ma, "session_manager", NULL);
154 
155     if (!e && !getenv("SESSION_MANAGER")) {
156         pa_log("X11 session manager not running.");
157         goto fail;
158     }
159 
160     memset(&callbacks, 0, sizeof(callbacks));
161     callbacks.die.callback = die_cb;
162     callbacks.die.client_data = u;
163     callbacks.save_yourself.callback = save_yourself_cb;
164     callbacks.save_yourself.client_data = m->core;
165     callbacks.save_complete.callback = save_complete_cb;
166     callbacks.save_complete.client_data = m->core;
167     callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb;
168     callbacks.shutdown_cancelled.client_data = m->core;
169 
170     if (!(u->connection = SmcOpenConnection(
171                   (char*) e, m->core,
172                   SmProtoMajor, SmProtoMinor,
173                   SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
174                   &callbacks, NULL, &client_id,
175                   sizeof(t), t))) {
176 
177         pa_log("Failed to open connection to session manager: %s", t);
178         goto fail;
179     }
180 
181     prop_program.name = (char*) SmProgram;
182     prop_program.type = (char*) SmARRAY8;
183     val_program.value = (char*) PACKAGE_NAME;
184     val_program.length = (int) strlen(val_program.value);
185     prop_program.num_vals = 1;
186     prop_program.vals = &val_program;
187     prop_list[0] = &prop_program;
188 
189     prop_user.name = (char*) SmUserID;
190     prop_user.type = (char*) SmARRAY8;
191     pa_get_user_name(t, sizeof(t));
192     val_user.value = t;
193     val_user.length = (int) strlen(val_user.value);
194     prop_user.num_vals = 1;
195     prop_user.vals = &val_user;
196     prop_list[1] = &prop_user;
197 
198     SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
199 
200     pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
201 
202     pa_client_new_data_init(&data);
203     data.module = m;
204     data.driver = __FILE__;
205     pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id);
206     pa_proplist_sets(data.proplist, "xsmp.vendor", vendor);
207     pa_proplist_sets(data.proplist, "xsmp.client.id", client_id);
208     u->client = pa_client_new(u->core, &data);
209     pa_client_new_data_done(&data);
210 
211     free(vendor);
212     free(client_id);
213 
214     if (!u->client)
215         goto fail;
216 
217     /* Positive exit_idle_time is only useful when we have no session tracking
218      * capability, so we can set it to 0 now that we have detected a session.
219      * The benefit of setting exit_idle_time to 0 is that pulseaudio will exit
220      * immediately when the session ends. That in turn is useful, because some
221      * systems (those that use pam_systemd but don't use systemd for managing
222      * pulseaudio) clean $XDG_RUNTIME_DIR on logout, but fail to terminate all
223      * services that depend on the files in $XDG_RUNTIME_DIR. The directory
224      * contains our sockets, and if the sockets are removed without terminating
225      * pulseaudio, a quick relogin will likely cause trouble, because a new
226      * instance will be spawned while the old instance is still running. */
227     if (u->core->exit_idle_time > 0)
228         pa_core_set_exit_idle_time(u->core, 0);
229 
230     pa_modargs_free(ma);
231 
232     return 0;
233 
234 fail:
235     if (ma)
236         pa_modargs_free(ma);
237 
238     pa__done(m);
239 
240     return -1;
241 }
242 
pa__done(pa_module * m)243 void pa__done(pa_module*m) {
244     struct userdata *u;
245 
246     pa_assert(m);
247 
248     if ((u = m->userdata)) {
249 
250         if (u->connection)
251             SmcCloseConnection(u->connection, 0, NULL);
252 
253         if (u->client)
254             pa_client_free(u->client);
255 
256         if (u->x11)
257             pa_x11_wrapper_unref(u->x11);
258 
259         pa_xfree(u);
260     }
261 
262     if (ice_in_use) {
263         IceRemoveConnectionWatch(new_ice_connection, m->core);
264         ice_in_use = false;
265     }
266 }
267