153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2006 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stdio.h> 2553a5a1b3Sopenharmony_ci#include <stdlib.h> 2653a5a1b3Sopenharmony_ci#include <string.h> 2753a5a1b3Sopenharmony_ci#include <unistd.h> 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include <xcb/xcb.h> 3053a5a1b3Sopenharmony_ci 3153a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3253a5a1b3Sopenharmony_ci 3353a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 3453a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/x11wrap.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/native-common.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/auth-cookie.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/x11prop.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/strlist.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/protocol-native.h> 4353a5a1b3Sopenharmony_ci 4453a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering"); 4553a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("X11 credential publisher"); 4653a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 4753a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 4853a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 4953a5a1b3Sopenharmony_ci "display=<X11 display> " 5053a5a1b3Sopenharmony_ci "sink=<Sink to publish> " 5153a5a1b3Sopenharmony_ci "source=<Source to publish> " 5253a5a1b3Sopenharmony_ci "cookie=<Cookie file to publish> "); 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 5553a5a1b3Sopenharmony_ci "display", 5653a5a1b3Sopenharmony_ci "sink", 5753a5a1b3Sopenharmony_ci "source", 5853a5a1b3Sopenharmony_ci "cookie", 5953a5a1b3Sopenharmony_ci "xauthority", 6053a5a1b3Sopenharmony_ci NULL 6153a5a1b3Sopenharmony_ci}; 6253a5a1b3Sopenharmony_ci 6353a5a1b3Sopenharmony_cistruct userdata { 6453a5a1b3Sopenharmony_ci pa_core *core; 6553a5a1b3Sopenharmony_ci pa_module *module; 6653a5a1b3Sopenharmony_ci pa_native_protocol *protocol; 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci char *id; 6953a5a1b3Sopenharmony_ci pa_auth_cookie *auth_cookie; 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_ci pa_x11_wrapper *x11_wrapper; 7253a5a1b3Sopenharmony_ci pa_x11_client *x11_client; 7353a5a1b3Sopenharmony_ci 7453a5a1b3Sopenharmony_ci pa_hook_slot *hook_slot; 7553a5a1b3Sopenharmony_ci}; 7653a5a1b3Sopenharmony_ci 7753a5a1b3Sopenharmony_cistatic void publish_servers(struct userdata *u, pa_strlist *l) { 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_ci int screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper)); 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_ci if (l) { 8253a5a1b3Sopenharmony_ci char *s; 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_ci l = pa_strlist_reverse(l); 8553a5a1b3Sopenharmony_ci s = pa_strlist_to_string(l); 8653a5a1b3Sopenharmony_ci pa_strlist_reverse(l); 8753a5a1b3Sopenharmony_ci 8853a5a1b3Sopenharmony_ci pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SERVER", s); 8953a5a1b3Sopenharmony_ci pa_xfree(s); 9053a5a1b3Sopenharmony_ci } else 9153a5a1b3Sopenharmony_ci pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SERVER"); 9253a5a1b3Sopenharmony_ci} 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_cistatic pa_hook_result_t servers_changed_cb(void *hook_data, void *call_data, void *slot_data) { 9553a5a1b3Sopenharmony_ci pa_strlist *servers = call_data; 9653a5a1b3Sopenharmony_ci struct userdata *u = slot_data; 9753a5a1b3Sopenharmony_ci char t[256]; 9853a5a1b3Sopenharmony_ci int screen; 9953a5a1b3Sopenharmony_ci 10053a5a1b3Sopenharmony_ci pa_assert(u); 10153a5a1b3Sopenharmony_ci 10253a5a1b3Sopenharmony_ci screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper)); 10353a5a1b3Sopenharmony_ci if (!pa_x11_get_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID", t, sizeof(t)) || !pa_streq(t, u->id)) { 10453a5a1b3Sopenharmony_ci pa_log_warn("PulseAudio information vanished from X11!"); 10553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 10653a5a1b3Sopenharmony_ci } 10753a5a1b3Sopenharmony_ci 10853a5a1b3Sopenharmony_ci publish_servers(u, servers); 10953a5a1b3Sopenharmony_ci return PA_HOOK_OK; 11053a5a1b3Sopenharmony_ci} 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_cistatic void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { 11353a5a1b3Sopenharmony_ci struct userdata *u = userdata; 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_ci pa_assert(w); 11653a5a1b3Sopenharmony_ci pa_assert(u); 11753a5a1b3Sopenharmony_ci pa_assert(u->x11_wrapper == w); 11853a5a1b3Sopenharmony_ci 11953a5a1b3Sopenharmony_ci pa_log_debug("X11 client kill callback called"); 12053a5a1b3Sopenharmony_ci 12153a5a1b3Sopenharmony_ci if (u->x11_client) 12253a5a1b3Sopenharmony_ci pa_x11_client_free(u->x11_client); 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci if (u->x11_wrapper) 12553a5a1b3Sopenharmony_ci pa_x11_wrapper_unref(u->x11_wrapper); 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci u->x11_client = NULL; 12853a5a1b3Sopenharmony_ci u->x11_wrapper = NULL; 12953a5a1b3Sopenharmony_ci 13053a5a1b3Sopenharmony_ci pa_module_unload_request(u->module, true); 13153a5a1b3Sopenharmony_ci} 13253a5a1b3Sopenharmony_ci 13353a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 13453a5a1b3Sopenharmony_ci struct userdata *u; 13553a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 13653a5a1b3Sopenharmony_ci char *mid, *sid; 13753a5a1b3Sopenharmony_ci char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; 13853a5a1b3Sopenharmony_ci const char *t; 13953a5a1b3Sopenharmony_ci int screen; 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_ci pa_assert(m); 14253a5a1b3Sopenharmony_ci 14353a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 14453a5a1b3Sopenharmony_ci pa_log("failed to parse module arguments"); 14553a5a1b3Sopenharmony_ci goto fail; 14653a5a1b3Sopenharmony_ci } 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew(struct userdata, 1); 14953a5a1b3Sopenharmony_ci u->core = m->core; 15053a5a1b3Sopenharmony_ci u->module = m; 15153a5a1b3Sopenharmony_ci u->protocol = pa_native_protocol_get(m->core); 15253a5a1b3Sopenharmony_ci u->id = NULL; 15353a5a1b3Sopenharmony_ci u->auth_cookie = NULL; 15453a5a1b3Sopenharmony_ci u->x11_client = NULL; 15553a5a1b3Sopenharmony_ci u->x11_wrapper = NULL; 15653a5a1b3Sopenharmony_ci 15753a5a1b3Sopenharmony_ci u->hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_SERVERS_CHANGED], PA_HOOK_NORMAL, servers_changed_cb, u); 15853a5a1b3Sopenharmony_ci 15953a5a1b3Sopenharmony_ci if (!(u->auth_cookie = pa_auth_cookie_get(m->core, pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), true, PA_NATIVE_COOKIE_LENGTH))) 16053a5a1b3Sopenharmony_ci goto fail; 16153a5a1b3Sopenharmony_ci 16253a5a1b3Sopenharmony_ci if (pa_modargs_get_value(ma, "xauthority", NULL)) { 16353a5a1b3Sopenharmony_ci if (setenv("XAUTHORITY", pa_modargs_get_value(ma, "xauthority", NULL), 1)) { 16453a5a1b3Sopenharmony_ci pa_log("setenv() for $XAUTHORITY failed"); 16553a5a1b3Sopenharmony_ci goto fail; 16653a5a1b3Sopenharmony_ci } 16753a5a1b3Sopenharmony_ci } 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) 17053a5a1b3Sopenharmony_ci goto fail; 17153a5a1b3Sopenharmony_ci 17253a5a1b3Sopenharmony_ci screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper)); 17353a5a1b3Sopenharmony_ci mid = pa_machine_id(); 17453a5a1b3Sopenharmony_ci u->id = pa_sprintf_malloc("%lu@%s/%lu", (unsigned long) getuid(), mid, (unsigned long) getpid()); 17553a5a1b3Sopenharmony_ci pa_xfree(mid); 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID", u->id); 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci if ((sid = pa_session_id())) { 18053a5a1b3Sopenharmony_ci pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SESSION_ID", sid); 18153a5a1b3Sopenharmony_ci pa_xfree(sid); 18253a5a1b3Sopenharmony_ci } 18353a5a1b3Sopenharmony_ci 18453a5a1b3Sopenharmony_ci publish_servers(u, pa_native_protocol_servers(u->protocol)); 18553a5a1b3Sopenharmony_ci 18653a5a1b3Sopenharmony_ci if ((t = pa_modargs_get_value(ma, "source", NULL))) 18753a5a1b3Sopenharmony_ci pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SOURCE", t); 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci if ((t = pa_modargs_get_value(ma, "sink", NULL))) 19053a5a1b3Sopenharmony_ci pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SINK", t); 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ci pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_COOKIE", 19353a5a1b3Sopenharmony_ci pa_hexstr(pa_auth_cookie_read(u->auth_cookie, PA_NATIVE_COOKIE_LENGTH), PA_NATIVE_COOKIE_LENGTH, hx, sizeof(hx))); 19453a5a1b3Sopenharmony_ci 19553a5a1b3Sopenharmony_ci u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u); 19653a5a1b3Sopenharmony_ci 19753a5a1b3Sopenharmony_ci pa_modargs_free(ma); 19853a5a1b3Sopenharmony_ci 19953a5a1b3Sopenharmony_ci return 0; 20053a5a1b3Sopenharmony_ci 20153a5a1b3Sopenharmony_cifail: 20253a5a1b3Sopenharmony_ci if (ma) 20353a5a1b3Sopenharmony_ci pa_modargs_free(ma); 20453a5a1b3Sopenharmony_ci 20553a5a1b3Sopenharmony_ci pa__done(m); 20653a5a1b3Sopenharmony_ci 20753a5a1b3Sopenharmony_ci return -1; 20853a5a1b3Sopenharmony_ci} 20953a5a1b3Sopenharmony_ci 21053a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 21153a5a1b3Sopenharmony_ci struct userdata*u; 21253a5a1b3Sopenharmony_ci 21353a5a1b3Sopenharmony_ci pa_assert(m); 21453a5a1b3Sopenharmony_ci 21553a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 21653a5a1b3Sopenharmony_ci return; 21753a5a1b3Sopenharmony_ci 21853a5a1b3Sopenharmony_ci if (u->x11_client) 21953a5a1b3Sopenharmony_ci pa_x11_client_free(u->x11_client); 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci if (u->x11_wrapper) { 22253a5a1b3Sopenharmony_ci char t[256]; 22353a5a1b3Sopenharmony_ci int screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper)); 22453a5a1b3Sopenharmony_ci 22553a5a1b3Sopenharmony_ci /* Yes, here is a race condition */ 22653a5a1b3Sopenharmony_ci if (!pa_x11_get_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID", t, sizeof(t)) || !pa_streq(t, u->id)) 22753a5a1b3Sopenharmony_ci pa_log_warn("PulseAudio information vanished from X11!"); 22853a5a1b3Sopenharmony_ci else { 22953a5a1b3Sopenharmony_ci pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID"); 23053a5a1b3Sopenharmony_ci pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SERVER"); 23153a5a1b3Sopenharmony_ci pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SINK"); 23253a5a1b3Sopenharmony_ci pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SOURCE"); 23353a5a1b3Sopenharmony_ci pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_COOKIE"); 23453a5a1b3Sopenharmony_ci pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SESSION_ID"); 23553a5a1b3Sopenharmony_ci xcb_flush(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper)); 23653a5a1b3Sopenharmony_ci } 23753a5a1b3Sopenharmony_ci 23853a5a1b3Sopenharmony_ci pa_x11_wrapper_unref(u->x11_wrapper); 23953a5a1b3Sopenharmony_ci } 24053a5a1b3Sopenharmony_ci 24153a5a1b3Sopenharmony_ci if (u->auth_cookie) 24253a5a1b3Sopenharmony_ci pa_auth_cookie_unref(u->auth_cookie); 24353a5a1b3Sopenharmony_ci 24453a5a1b3Sopenharmony_ci if (u->hook_slot) 24553a5a1b3Sopenharmony_ci pa_hook_slot_free(u->hook_slot); 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci if (u->protocol) 24853a5a1b3Sopenharmony_ci pa_native_protocol_unref(u->protocol); 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci pa_xfree(u->id); 25153a5a1b3Sopenharmony_ci pa_xfree(u); 25253a5a1b3Sopenharmony_ci} 253