/* Can be used in either PA or Avahi mainloop context since the bits of u->core * that we access don't change after startup. */ static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) { char s[128]; char *t; pa_assert(c); l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION); t = pa_get_user_name_malloc(); l = avahi_string_list_add_pair(l, "user-name", t); pa_xfree(t); t = pa_machine_id(); l = avahi_string_list_add_pair(l, "machine-id", t); pa_xfree(t); t = pa_uname_string(); l = avahi_string_list_add_pair(l, "uname", t); pa_xfree(t); l = avahi_string_list_add_pair(l, "fqdn", pa_get_fqdn(s, sizeof(s))); l = avahi_string_list_add_printf(l, "cookie=0x%08x", c->cookie); return l; }
static void txt_record_server_data(pa_core *c, TXTRecordRef *txt) { char s[128]; char *t; pa_assert(c); TXTRecordSetValue(txt, "server-version", strlen(PACKAGE_NAME" "PACKAGE_VERSION), PACKAGE_NAME" "PACKAGE_VERSION); t = pa_get_user_name_malloc(); TXTRecordSetValue(txt, "user-name", strlen(t), t); pa_xfree(t); t = pa_machine_id(); TXTRecordSetValue(txt, "machine-id", strlen(t), t); pa_xfree(t); t = pa_uname_string(); TXTRecordSetValue(txt, "uname", strlen(t), t); pa_xfree(t); t = pa_get_fqdn(s, sizeof(s)); TXTRecordSetValue(txt, "fqdn", strlen(t), t); snprintf(s, sizeof(s), "0x%08x", c->cookie); TXTRecordSetValue(txt, "cookie", strlen(s), s); }
char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) { pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); pa_assert(c); pa_assert(l > 0); switch (s->type) { #ifdef HAVE_IPV6 case SOCKET_SERVER_IPV6: { struct sockaddr_in6 sa; socklen_t sa_len = sizeof(sa); if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { pa_log("getsockname(): %s", pa_cstrerror(errno)); return NULL; } if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) { char fqdn[256]; if (!pa_get_fqdn(fqdn, sizeof(fqdn))) return NULL; pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port)); } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) { char *id; if (!(id = pa_machine_id())) return NULL; pa_snprintf(c, l, "{%s}tcp6:localhost:%u", id, (unsigned) ntohs(sa.sin6_port)); pa_xfree(id); } else { char ip[INET6_ADDRSTRLEN]; if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) { pa_log("inet_ntop(): %s", pa_cstrerror(errno)); return NULL; } pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port)); } return c; } #endif case SOCKET_SERVER_IPV4: { struct sockaddr_in sa; socklen_t sa_len = sizeof(sa); if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { pa_log("getsockname(): %s", pa_cstrerror(errno)); return NULL; } if (sa.sin_addr.s_addr == INADDR_ANY) { char fqdn[256]; if (!pa_get_fqdn(fqdn, sizeof(fqdn))) return NULL; pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port)); } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) { char *id; if (!(id = pa_machine_id())) return NULL; pa_snprintf(c, l, "{%s}tcp:localhost:%u", id, (unsigned) ntohs(sa.sin_port)); pa_xfree(id); } else { char ip[INET_ADDRSTRLEN]; if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) { pa_log("inet_ntop(): %s", pa_cstrerror(errno)); return NULL; } pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port)); } return c; } case SOCKET_SERVER_UNIX: { char *id; if (!s->filename) return NULL; if (!(id = pa_machine_id())) return NULL; pa_snprintf(c, l, "{%s}unix:%s", id, s->filename); pa_xfree(id); return c; } default: return NULL; } }
int main(int argc, char *argv[]) { const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE; int c, ret = 1; Display *d = NULL; enum { DUMP, EXPORT, IMPORT, REMOVE } mode = DUMP; while ((c = getopt(argc, argv, "deiD:S:O:I:c:hr")) != -1) { switch (c) { case 'D' : dname = optarg; break; case 'h': printf("%s [-D display] [-S server] [-O sink] [-I source] [-c file] [-d|-e|-i|-r]\n\n" " -d Show current PulseAudio data attached to X11 display (default)\n" " -e Export local PulseAudio data to X11 display\n" " -i Import PulseAudio data from X11 display to local environment variables and cookie file.\n" " -r Remove PulseAudio data from X11 display\n", pa_path_get_filename(argv[0])); ret = 0; goto finish; case 'd': mode = DUMP; break; case 'e': mode = EXPORT; break; case 'i': mode = IMPORT; break; case 'r': mode = REMOVE; break; case 'c': cookie_file = optarg; break; case 'I': source = optarg; break; case 'O': sink = optarg; break; case 'S': server = optarg; break; default: fprintf(stderr, "Failed to parse command line.\n"); goto finish; } } if (!(d = XOpenDisplay(dname))) { pa_log("XOpenDisplay() failed"); goto finish; } switch (mode) { case DUMP: { char t[1024]; if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t))) printf("Server: %s\n", t); if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t))) printf("Source: %s\n", t); if (pa_x11_get_prop(d, "PULSE_SINK", t, sizeof(t))) printf("Sink: %s\n", t); if (pa_x11_get_prop(d, "PULSE_COOKIE", t, sizeof(t))) printf("Cookie: %s\n", t); break; } case IMPORT: { char t[1024]; if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t))) printf("PULSE_SERVER='%s'\nexport PULSE_SERVER\n", t); if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t))) printf("PULSE_SOURCE='%s'\nexport PULSE_SOURCE\n", t); if (pa_x11_get_prop(d, "PULSE_SINK", t, sizeof(t))) printf("PULSE_SINK='%s'\nexport PULSE_SINK\n", t); if (pa_x11_get_prop(d, "PULSE_COOKIE", t, sizeof(t))) { uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; size_t l; if ((l = pa_parsehex(t, cookie, sizeof(cookie))) != sizeof(cookie)) { fprintf(stderr, "Failed to parse cookie data\n"); goto finish; } if (pa_authkey_save(cookie_file, cookie, l) < 0) { fprintf(stderr, "Failed to save cookie data\n"); goto finish; } } break; } case EXPORT: { pa_client_conf *conf = pa_client_conf_new(); uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; assert(conf); if (pa_client_conf_load(conf, NULL) < 0) { fprintf(stderr, "Failed to load client configuration file.\n"); goto finish; } if (pa_client_conf_env(conf) < 0) { fprintf(stderr, "Failed to read environment configuration data.\n"); goto finish; } pa_x11_del_prop(d, "PULSE_SERVER"); pa_x11_del_prop(d, "PULSE_SINK"); pa_x11_del_prop(d, "PULSE_SOURCE"); pa_x11_del_prop(d, "PULSE_ID"); pa_x11_del_prop(d, "PULSE_COOKIE"); if (server) pa_x11_set_prop(d, "PULSE_SERVER", server); else if (conf->default_server) pa_x11_set_prop(d, "PULSE_SERVER", conf->default_server); else { char hn[256]; if (!pa_get_fqdn(hn, sizeof(hn))) { fprintf(stderr, "Failed to get FQDN.\n"); goto finish; } pa_x11_set_prop(d, "PULSE_SERVER", hn); } if (sink) pa_x11_set_prop(d, "PULSE_SINK", sink); else if (conf->default_sink) pa_x11_set_prop(d, "PULSE_SINK", conf->default_sink); if (source) pa_x11_set_prop(d, "PULSE_SOURCE", source); if (conf->default_source) pa_x11_set_prop(d, "PULSE_SOURCE", conf->default_source); pa_client_conf_free(conf); if (pa_authkey_load_auto(cookie_file, cookie, sizeof(cookie)) < 0) { fprintf(stderr, "Failed to load cookie data\n"); goto finish; } pa_x11_set_prop(d, "PULSE_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx))); break; } case REMOVE: pa_x11_del_prop(d, "PULSE_SERVER"); pa_x11_del_prop(d, "PULSE_SINK"); pa_x11_del_prop(d, "PULSE_SOURCE"); pa_x11_del_prop(d, "PULSE_ID"); pa_x11_del_prop(d, "PULSE_COOKIE"); break; default: fprintf(stderr, "No yet implemented.\n"); goto finish; } ret = 0; finish: if (d) { XSync(d, False); XCloseDisplay(d); } return ret; }
int pa__init(pa_module*m) { struct userdata *u; pa_modargs *ma = NULL; const char *dst_addr; const char *src_addr; uint32_t port = DEFAULT_PORT, mtu; uint32_t ttl = DEFAULT_TTL; sa_family_t af; int fd = -1, sap_fd = -1; pa_source *s; pa_sample_spec ss; pa_channel_map cm; struct sockaddr_in dst_sa4, dst_sap_sa4, src_sa4, src_sap_sa4; #ifdef HAVE_IPV6 struct sockaddr_in6 dst_sa6, dst_sap_sa6, src_sa6, src_sap_sa6; #endif struct sockaddr_storage sa_dst; pa_source_output *o = NULL; uint8_t payload; char *p; int r, j; socklen_t k; char hn[128], *n; bool loop = false; enum inhibit_auto_suspend inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES; const char *inhibit_auto_suspend_str; pa_source_output_new_data data; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) { pa_log("Source does not exist."); goto fail; } if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) { pa_log("Failed to parse \"loop\" parameter."); goto fail; } if ((inhibit_auto_suspend_str = pa_modargs_get_value(ma, "inhibit_auto_suspend", NULL))) { if (pa_streq(inhibit_auto_suspend_str, "always")) inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ALWAYS; else if (pa_streq(inhibit_auto_suspend_str, "never")) inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_NEVER; else if (pa_streq(inhibit_auto_suspend_str, "only_with_non_monitor_sources")) inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES; else { pa_log("Failed to parse the \"inhibit_auto_suspend\" parameter."); goto fail; } } ss = s->sample_spec; pa_rtp_sample_spec_fixup(&ss); cm = s->channel_map; if (pa_modargs_get_sample_spec(ma, &ss) < 0) { pa_log("Failed to parse sample specification"); goto fail; } if (!pa_rtp_sample_spec_valid(&ss)) { pa_log("Specified sample type not compatible with RTP"); goto fail; } if (ss.channels != cm.channels) pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AIFF); payload = pa_rtp_payload_from_sample_spec(&ss); mtu = (uint32_t) pa_frame_align(DEFAULT_MTU, &ss); if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) { pa_log("Invalid MTU."); goto fail; } port = DEFAULT_PORT + ((uint32_t) (rand() % 512) << 1); if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) { pa_log("port= expects a numerical argument between 1 and 65535."); goto fail; } if (port & 1) pa_log_warn("Port number not even as suggested in RFC3550!"); if (pa_modargs_get_value_u32(ma, "ttl", &ttl) < 0 || ttl < 1 || ttl > 0xFF) { pa_log("ttl= expects a numerical argument between 1 and 255."); goto fail; } src_addr = pa_modargs_get_value(ma, "source_ip", DEFAULT_SOURCE_IP); if (inet_pton(AF_INET, src_addr, &src_sa4.sin_addr) > 0) { src_sa4.sin_family = af = AF_INET; src_sa4.sin_port = htons(0); memset(&src_sa4.sin_zero, 0, sizeof(src_sa4.sin_zero)); src_sap_sa4 = src_sa4; #ifdef HAVE_IPV6 } else if (inet_pton(AF_INET6, src_addr, &src_sa6.sin6_addr) > 0) { src_sa6.sin6_family = af = AF_INET6; src_sa6.sin6_port = htons(0); src_sa6.sin6_flowinfo = 0; src_sa6.sin6_scope_id = 0; src_sap_sa6 = src_sa6; #endif } else { pa_log("Invalid source address '%s'", src_addr); goto fail; } dst_addr = pa_modargs_get_value(ma, "destination", NULL); if (dst_addr == NULL) dst_addr = pa_modargs_get_value(ma, "destination_ip", DEFAULT_DESTINATION_IP); if (inet_pton(AF_INET, dst_addr, &dst_sa4.sin_addr) > 0) { dst_sa4.sin_family = af = AF_INET; dst_sa4.sin_port = htons((uint16_t) port); memset(&dst_sa4.sin_zero, 0, sizeof(dst_sa4.sin_zero)); dst_sap_sa4 = dst_sa4; dst_sap_sa4.sin_port = htons(SAP_PORT); #ifdef HAVE_IPV6 } else if (inet_pton(AF_INET6, dst_addr, &dst_sa6.sin6_addr) > 0) { dst_sa6.sin6_family = af = AF_INET6; dst_sa6.sin6_port = htons((uint16_t) port); dst_sa6.sin6_flowinfo = 0; dst_sa6.sin6_scope_id = 0; dst_sap_sa6 = dst_sa6; dst_sap_sa6.sin6_port = htons(SAP_PORT); #endif } else { pa_log("Invalid destination '%s'", dst_addr); goto fail; } if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) { pa_log("socket() failed: %s", pa_cstrerror(errno)); goto fail; } if (af == AF_INET && bind(fd, (struct sockaddr*) &src_sa4, sizeof(src_sa4)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && bind(fd, (struct sockaddr*) &src_sa6, sizeof(src_sa6)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #endif } if (af == AF_INET && connect(fd, (struct sockaddr*) &dst_sa4, sizeof(dst_sa4)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && connect(fd, (struct sockaddr*) &dst_sa6, sizeof(dst_sa6)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #endif } if ((sap_fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) { pa_log("socket() failed: %s", pa_cstrerror(errno)); goto fail; } if (af == AF_INET && bind(sap_fd, (struct sockaddr*) &src_sap_sa4, sizeof(src_sap_sa4)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && bind(sap_fd, (struct sockaddr*) &src_sap_sa6, sizeof(src_sap_sa6)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #endif } if (af == AF_INET && connect(sap_fd, (struct sockaddr*) &dst_sap_sa4, sizeof(dst_sap_sa4)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && connect(sap_fd, (struct sockaddr*) &dst_sap_sa6, sizeof(dst_sap_sa6)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #endif } j = loop; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0 || setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0) { pa_log("IP_MULTICAST_LOOP failed: %s", pa_cstrerror(errno)); goto fail; } if (ttl != DEFAULT_TTL) { int _ttl = (int) ttl; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) { pa_log("IP_MULTICAST_TTL failed: %s", pa_cstrerror(errno)); goto fail; } if (setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) { pa_log("IP_MULTICAST_TTL (sap) failed: %s", pa_cstrerror(errno)); goto fail; } } /* If the socket queue is full, let's drop packets */ pa_make_fd_nonblock(fd); pa_make_udp_socket_low_delay(fd); pa_source_output_new_data_init(&data); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream"); pa_proplist_sets(data.proplist, "rtp.source", src_addr); pa_proplist_sets(data.proplist, "rtp.destination", dst_addr); pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu); pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port); pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl); data.driver = __FILE__; data.module = m; pa_source_output_new_data_set_source(&data, s, false, true); pa_source_output_new_data_set_sample_spec(&data, &ss); pa_source_output_new_data_set_channel_map(&data, &cm); data.flags |= get_dont_inhibit_auto_suspend_flag(s, inhibit_auto_suspend); pa_source_output_new(&o, m->core, &data); pa_source_output_new_data_done(&data); if (!o) { pa_log("failed to create source output."); goto fail; } o->parent.process_msg = source_output_process_msg; o->push = source_output_push_cb; o->moving = source_output_moving_cb; o->kill = source_output_kill_cb; pa_log_info("Configured source latency of %llu ms.", (unsigned long long) pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC); m->userdata = o->userdata = u = pa_xnew(struct userdata, 1); u->module = m; u->source_output = o; u->memblockq = pa_memblockq_new( "module-rtp-send memblockq", 0, MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, &ss, 1, 0, 0, NULL); u->mtu = mtu; k = sizeof(sa_dst); pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0); n = pa_xstrdup(pa_modargs_get_value(ma, "stream_name", NULL)); if (n == NULL) n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn))); if (af == AF_INET) { p = pa_sdp_build(af, (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr, (void*) &dst_sa4.sin_addr, n, (uint16_t) port, payload, &ss); #ifdef HAVE_IPV6 } else { p = pa_sdp_build(af, (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr, (void*) &dst_sa6.sin6_addr, n, (uint16_t) port, payload, &ss); #endif } pa_xfree(n); pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss)); pa_sap_context_init_send(&u->sap_context, sap_fd, p); pa_log_info("RTP stream initialized with mtu %u on %s:%u from %s ttl=%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dst_addr, port, src_addr, ttl, u->rtp_context.ssrc, payload, u->rtp_context.sequence); pa_log_info("SDP-Data:\n%s\nEOF", p); pa_sap_send(&u->sap_context, 0); u->sap_event = pa_core_rttime_new(m->core, pa_rtclock_now() + SAP_INTERVAL, sap_event_cb, u); u->inhibit_auto_suspend = inhibit_auto_suspend; pa_source_output_put(u->source_output); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); if (fd >= 0) pa_close(fd); if (sap_fd >= 0) pa_close(sap_fd); return -1; }
int main(int argc, char *argv[]) { const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE; int c, ret = 1, screen = 0; xcb_connection_t *xcb = NULL; enum { DUMP, EXPORT, IMPORT, REMOVE } mode = DUMP; setlocale(LC_ALL, ""); #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR); #endif while ((c = getopt(argc, argv, "deiD:S:O:I:c:hr")) != -1) { switch (c) { case 'D' : dname = optarg; break; case 'h': printf(_("%s [-D display] [-S server] [-O sink] [-I source] [-c file] [-d|-e|-i|-r]\n\n" " -d Show current PulseAudio data attached to X11 display (default)\n" " -e Export local PulseAudio data to X11 display\n" " -i Import PulseAudio data from X11 display to local environment variables and cookie file.\n" " -r Remove PulseAudio data from X11 display\n"), pa_path_get_filename(argv[0])); ret = 0; goto finish; case 'd': mode = DUMP; break; case 'e': mode = EXPORT; break; case 'i': mode = IMPORT; break; case 'r': mode = REMOVE; break; case 'c': cookie_file = optarg; break; case 'I': source = optarg; break; case 'O': sink = optarg; break; case 'S': server = optarg; break; default: fprintf(stderr, _("Failed to parse command line.\n")); goto finish; } } if (!(xcb = xcb_connect(dname, &screen))) { pa_log(_("xcb_connect() failed")); goto finish; } if (xcb_connection_has_error(xcb)) { pa_log(_("xcb_connection_has_error() returned true")); goto finish; } switch (mode) { case DUMP: { char t[1024]; if (pa_x11_get_prop(xcb, screen, "PULSE_SERVER", t, sizeof(t))) printf(_("Server: %s\n"), t); if (pa_x11_get_prop(xcb, screen, "PULSE_SOURCE", t, sizeof(t))) printf(_("Source: %s\n"), t); if (pa_x11_get_prop(xcb, screen, "PULSE_SINK", t, sizeof(t))) printf(_("Sink: %s\n"), t); if (pa_x11_get_prop(xcb, screen, "PULSE_COOKIE", t, sizeof(t))) printf(_("Cookie: %s\n"), t); break; } case IMPORT: { char t[1024]; if (pa_x11_get_prop(xcb, screen, "PULSE_SERVER", t, sizeof(t))) printf("PULSE_SERVER='%s'\nexport PULSE_SERVER\n", t); if (pa_x11_get_prop(xcb, screen, "PULSE_SOURCE", t, sizeof(t))) printf("PULSE_SOURCE='%s'\nexport PULSE_SOURCE\n", t); if (pa_x11_get_prop(xcb, screen, "PULSE_SINK", t, sizeof(t))) printf("PULSE_SINK='%s'\nexport PULSE_SINK\n", t); if (pa_x11_get_prop(xcb, screen, "PULSE_COOKIE", t, sizeof(t))) { uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; size_t l; if ((l = pa_parsehex(t, cookie, sizeof(cookie))) != sizeof(cookie)) { fprintf(stderr, _("Failed to parse cookie data\n")); goto finish; } if (pa_authkey_save(cookie_file, cookie, l) < 0) { fprintf(stderr, _("Failed to save cookie data\n")); goto finish; } } break; } case EXPORT: { pa_client_conf *conf = pa_client_conf_new(); uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; assert(conf); pa_client_conf_load(conf, false, true); pa_x11_del_prop(xcb, screen, "PULSE_SERVER"); pa_x11_del_prop(xcb, screen, "PULSE_SINK"); pa_x11_del_prop(xcb, screen, "PULSE_SOURCE"); pa_x11_del_prop(xcb, screen, "PULSE_ID"); pa_x11_del_prop(xcb, screen, "PULSE_COOKIE"); if (server) pa_x11_set_prop(xcb, screen, "PULSE_SERVER", server); else if (conf->default_server) pa_x11_set_prop(xcb, screen, "PULSE_SERVER", conf->default_server); else { char hn[256]; if (!pa_get_fqdn(hn, sizeof(hn))) { fprintf(stderr, _("Failed to get FQDN.\n")); goto finish; } pa_x11_set_prop(xcb, screen, "PULSE_SERVER", hn); } if (sink) pa_x11_set_prop(xcb, screen, "PULSE_SINK", sink); else if (conf->default_sink) pa_x11_set_prop(xcb, screen, "PULSE_SINK", conf->default_sink); if (source) pa_x11_set_prop(xcb, screen, "PULSE_SOURCE", source); if (conf->default_source) pa_x11_set_prop(xcb, screen, "PULSE_SOURCE", conf->default_source); pa_client_conf_free(conf); if (pa_authkey_load(cookie_file, true, cookie, sizeof(cookie)) < 0) { fprintf(stderr, _("Failed to load cookie data\n")); goto finish; } pa_x11_set_prop(xcb, screen, "PULSE_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx))); break; } case REMOVE: pa_x11_del_prop(xcb, screen, "PULSE_SERVER"); pa_x11_del_prop(xcb, screen, "PULSE_SINK"); pa_x11_del_prop(xcb, screen, "PULSE_SOURCE"); pa_x11_del_prop(xcb, screen, "PULSE_ID"); pa_x11_del_prop(xcb, screen, "PULSE_COOKIE"); pa_x11_del_prop(xcb, screen, "PULSE_SESSION_ID"); break; default: fprintf(stderr, _("Not yet implemented.\n")); goto finish; } ret = 0; finish: if (xcb) { xcb_flush(xcb); xcb_disconnect(xcb); } return ret; }