]> code.delx.au - pulseaudio/blob - src/modules/module-tunnel.c
* modify pa_context_exit_daemon() to return a pa_operation object
[pulseaudio] / src / modules / module-tunnel.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio 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 of the License,
9 or (at your option) any later version.
10
11 polypaudio 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 polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 #include <polypcore/module.h>
35 #include <polypcore/util.h>
36 #include <polypcore/modargs.h>
37 #include <polypcore/log.h>
38 #include <polypcore/core-subscribe.h>
39 #include <polypcore/xmalloc.h>
40 #include <polypcore/sink-input.h>
41 #include <polypcore/pdispatch.h>
42 #include <polypcore/pstream.h>
43 #include <polypcore/pstream-util.h>
44 #include <polypcore/authkey.h>
45 #include <polypcore/socket-client.h>
46 #include <polypcore/socket-util.h>
47 #include <polypcore/authkey-prop.h>
48
49 #ifdef TUNNEL_SINK
50 #include "module-tunnel-sink-symdef.h"
51 PA_MODULE_DESCRIPTION("Tunnel module for sinks")
52 PA_MODULE_USAGE("server=<address> sink=<remote sink name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> sink_name=<name for the local sink>")
53 #else
54 #include "module-tunnel-source-symdef.h"
55 PA_MODULE_DESCRIPTION("Tunnel module for sources")
56 PA_MODULE_USAGE("server=<address> source=<remote source name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> source_name=<name for the local source>")
57 #endif
58
59 PA_MODULE_AUTHOR("Lennart Poettering")
60 PA_MODULE_VERSION(PACKAGE_VERSION)
61
62 #define DEFAULT_SINK_NAME "tunnel"
63 #define DEFAULT_SOURCE_NAME "tunnel"
64
65 #define DEFAULT_TLENGTH (44100*2*2/10) //(10240*8)
66 #define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2)
67 #define DEFAULT_MINREQ 512
68 #define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ)
69 #define DEFAULT_FRAGSIZE 1024
70
71 #define DEFAULT_TIMEOUT 5
72
73 #define LATENCY_INTERVAL 10
74
75 static const char* const valid_modargs[] = {
76 "server",
77 "cookie",
78 "format",
79 "channels",
80 "rate",
81 #ifdef TUNNEL_SINK
82 "sink_name",
83 "sink",
84 #else
85 "source_name",
86 "source",
87 #endif
88 NULL,
89 };
90
91 static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
92
93 #ifdef TUNNEL_SINK
94 static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
95 #endif
96
97 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
98 #ifdef TUNNEL_SINK
99 [PA_COMMAND_REQUEST] = command_request,
100 #endif
101 [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
102 [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed
103 };
104
105 struct userdata {
106 pa_socket_client *client;
107 pa_pstream *pstream;
108 pa_pdispatch *pdispatch;
109
110 char *server_name;
111 #ifdef TUNNEL_SINK
112 char *sink_name;
113 pa_sink *sink;
114 uint32_t requested_bytes;
115 #else
116 char *source_name;
117 pa_source *source;
118 #endif
119
120 pa_module *module;
121 pa_core *core;
122
123 uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
124
125 uint32_t ctag;
126 uint32_t device_index;
127 uint32_t channel;
128
129 pa_usec_t host_latency;
130
131 pa_time_event *time_event;
132
133 int auth_cookie_in_property;
134 };
135
136 static void close_stuff(struct userdata *u) {
137 assert(u);
138
139 if (u->pstream) {
140 pa_pstream_close(u->pstream);
141 pa_pstream_unref(u->pstream);
142 u->pstream = NULL;
143 }
144
145 if (u->pdispatch) {
146 pa_pdispatch_unref(u->pdispatch);
147 u->pdispatch = NULL;
148 }
149
150 if (u->client) {
151 pa_socket_client_unref(u->client);
152 u->client = NULL;
153 }
154
155 #ifdef TUNNEL_SINK
156 if (u->sink) {
157 pa_sink_disconnect(u->sink);
158 pa_sink_unref(u->sink);
159 u->sink = NULL;
160 }
161 #else
162 if (u->source) {
163 pa_source_disconnect(u->source);
164 pa_source_unref(u->source);
165 u->source = NULL;
166 }
167 #endif
168
169 if (u->time_event) {
170 u->core->mainloop->time_free(u->time_event);
171 u->time_event = NULL;
172 }
173 }
174
175 static void die(struct userdata *u) {
176 assert(u);
177 close_stuff(u);
178 pa_module_unload_request(u->module);
179 }
180
181 static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
182 struct userdata *u = userdata;
183 assert(pd && t && u && u->pdispatch == pd);
184
185 pa_log(__FILE__": stream killed\n");
186 die(u);
187 }
188
189 #ifdef TUNNEL_SINK
190 static void send_prebuf_request(struct userdata *u) {
191 pa_tagstruct *t;
192
193 t = pa_tagstruct_new(NULL, 0);
194 pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM);
195 pa_tagstruct_putu32(t, u->ctag++);
196 pa_tagstruct_putu32(t, u->channel);
197 pa_pstream_send_tagstruct(u->pstream, t);
198 }
199
200 static void send_bytes(struct userdata *u) {
201 assert(u);
202
203 if (!u->pstream)
204 return;
205
206 while (u->requested_bytes > 0) {
207 pa_memchunk chunk;
208 if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) {
209
210
211 if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF)
212 send_prebuf_request(u);
213
214 return;
215 }
216
217 pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, &chunk);
218 pa_memblock_unref(chunk.memblock);
219
220 if (chunk.length > u->requested_bytes)
221 u->requested_bytes = 0;
222 else
223 u->requested_bytes -= chunk.length;
224 }
225 }
226
227 static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
228 struct userdata *u = userdata;
229 uint32_t bytes, channel;
230 assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd);
231
232 if (pa_tagstruct_getu32(t, &channel) < 0 ||
233 pa_tagstruct_getu32(t, &bytes) < 0 ||
234 !pa_tagstruct_eof(t)) {
235 pa_log(__FILE__": invalid protocol reply\n");
236 die(u);
237 return;
238 }
239
240 if (channel != u->channel) {
241 pa_log(__FILE__": recieved data for invalid channel\n");
242 die(u);
243 return;
244 }
245
246 u->requested_bytes += bytes;
247 send_bytes(u);
248 }
249
250 #endif
251
252 static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
253 struct userdata *u = userdata;
254 pa_usec_t buffer_usec, sink_usec, source_usec, transport_usec;
255 int playing;
256 uint32_t queue_length;
257 uint64_t counter;
258 struct timeval local, remote, now;
259 assert(pd && u);
260
261 if (command != PA_COMMAND_REPLY) {
262 if (command == PA_COMMAND_ERROR)
263 pa_log(__FILE__": failed to get latency.\n");
264 else
265 pa_log(__FILE__": protocol error.\n");
266 die(u);
267 return;
268 }
269
270 if (pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
271 pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
272 pa_tagstruct_get_usec(t, &source_usec) < 0 ||
273 pa_tagstruct_get_boolean(t, &playing) < 0 ||
274 pa_tagstruct_getu32(t, &queue_length) < 0 ||
275 pa_tagstruct_get_timeval(t, &local) < 0 ||
276 pa_tagstruct_get_timeval(t, &remote) < 0 ||
277 pa_tagstruct_getu64(t, &counter) < 0 ||
278 !pa_tagstruct_eof(t)) {
279 pa_log(__FILE__": invalid reply.\n");
280 die(u);
281 return;
282 }
283
284 pa_gettimeofday(&now);
285
286 if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
287 /* local and remote seem to have synchronized clocks */
288 #ifdef TUNNEL_SINK
289 transport_usec = pa_timeval_diff(&remote, &local);
290 #else
291 transport_usec = pa_timeval_diff(&now, &remote);
292 #endif
293 } else
294 transport_usec = pa_timeval_diff(&now, &local)/2;
295
296 #ifdef TUNNEL_SINK
297 u->host_latency = sink_usec + transport_usec;
298 #else
299 u->host_latency = source_usec + transport_usec;
300 if (u->host_latency > sink_usec)
301 u->host_latency -= sink_usec;
302 else
303 u->host_latency = 0;
304 #endif
305
306 /* pa_log(__FILE__": estimated host latency: %0.0f usec\n", (double) u->host_latency); */
307 }
308
309 static void request_latency(struct userdata *u) {
310 pa_tagstruct *t;
311 struct timeval now;
312 uint32_t tag;
313 assert(u);
314
315 t = pa_tagstruct_new(NULL, 0);
316 #ifdef TUNNEL_SINK
317 pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
318 #else
319 pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
320 #endif
321 pa_tagstruct_putu32(t, tag = u->ctag++);
322 pa_tagstruct_putu32(t, u->channel);
323
324 pa_gettimeofday(&now);
325 pa_tagstruct_put_timeval(t, &now);
326 pa_tagstruct_putu64(t, 0);
327
328 pa_pstream_send_tagstruct(u->pstream, t);
329 pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u);
330 }
331
332 static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
333 struct userdata *u = userdata;
334 assert(pd && u && u->pdispatch == pd);
335
336 if (command != PA_COMMAND_REPLY) {
337 if (command == PA_COMMAND_ERROR)
338 pa_log(__FILE__": failed to create stream.\n");
339 else
340 pa_log(__FILE__": protocol error.\n");
341 die(u);
342 return;
343 }
344
345 if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
346 pa_tagstruct_getu32(t, &u->device_index) < 0 ||
347 #ifdef TUNNEL_SINK
348 pa_tagstruct_getu32(t, &u->requested_bytes) < 0 ||
349 #endif
350 !pa_tagstruct_eof(t)) {
351 pa_log(__FILE__": invalid reply.\n");
352 die(u);
353 return;
354 }
355
356 request_latency(u);
357 #ifdef TUNNEL_SINK
358 send_bytes(u);
359 #endif
360 }
361
362 static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
363 struct userdata *u = userdata;
364 pa_tagstruct *reply;
365 char name[256], un[128], hn[128];
366 assert(pd && u && u->pdispatch == pd);
367
368 if (command != PA_COMMAND_REPLY || !pa_tagstruct_eof(t)) {
369 if (command == PA_COMMAND_ERROR)
370 pa_log(__FILE__": failed to authenticate\n");
371 else
372 pa_log(__FILE__": protocol error.\n");
373 die(u);
374 return;
375 }
376 #ifdef TUNNEL_SINK
377 snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', sink '%s'",
378 pa_get_host_name(hn, sizeof(hn)),
379 pa_get_user_name(un, sizeof(un)),
380 u->sink->name);
381 #else
382 snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', source '%s'",
383 pa_get_host_name(hn, sizeof(hn)),
384 pa_get_user_name(un, sizeof(un)),
385 u->source->name);
386 #endif
387
388 reply = pa_tagstruct_new(NULL, 0);
389 pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
390 pa_tagstruct_putu32(reply, tag = u->ctag++);
391 pa_tagstruct_puts(reply, name);
392 pa_pstream_send_tagstruct(u->pstream, reply);
393 /* We ignore the server's reply here */
394
395 reply = pa_tagstruct_new(NULL, 0);
396 #ifdef TUNNEL_SINK
397 pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
398 pa_tagstruct_putu32(reply, tag = u->ctag++);
399 pa_tagstruct_puts(reply, name);
400 pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
401 pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
402 pa_tagstruct_puts(reply, u->sink_name);
403 pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
404 pa_tagstruct_put_boolean(reply, 0);
405 pa_tagstruct_putu32(reply, DEFAULT_TLENGTH);
406 pa_tagstruct_putu32(reply, DEFAULT_PREBUF);
407 pa_tagstruct_putu32(reply, DEFAULT_MINREQ);
408 pa_tagstruct_putu32(reply, PA_VOLUME_NORM);
409 #else
410 pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
411 pa_tagstruct_putu32(reply, tag = u->ctag++);
412 pa_tagstruct_puts(reply, name);
413 pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
414 pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
415 pa_tagstruct_puts(reply, u->source_name);
416 pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
417 pa_tagstruct_put_boolean(reply, 0);
418 pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE);
419 #endif
420
421 pa_pstream_send_tagstruct(u->pstream, reply);
422 pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u);
423 }
424
425 static void pstream_die_callback(pa_pstream *p, void *userdata) {
426 struct userdata *u = userdata;
427 assert(p && u);
428
429 pa_log(__FILE__": stream died.\n");
430 die(u);
431 }
432
433
434 static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) {
435 struct userdata *u = userdata;
436 assert(p && packet && u);
437
438 if (pa_pdispatch_run(u->pdispatch, packet, u) < 0) {
439 pa_log(__FILE__": invalid packet\n");
440 die(u);
441 }
442 }
443
444 #ifndef TUNNEL_SINK
445 static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
446 struct userdata *u = userdata;
447 assert(p && chunk && u);
448
449 if (channel != u->channel) {
450 pa_log(__FILE__": recieved memory block on bad channel.\n");
451 die(u);
452 return;
453 }
454
455 pa_source_post(u->source, chunk);
456 }
457 #endif
458
459 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
460 struct userdata *u = userdata;
461 pa_tagstruct *t;
462 uint32_t tag;
463 assert(sc && u && u->client == sc);
464
465 pa_socket_client_unref(u->client);
466 u->client = NULL;
467
468 if (!io) {
469 pa_log(__FILE__": connection failed.\n");
470 pa_module_unload_request(u->module);
471 return;
472 }
473
474 u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->memblock_stat);
475 u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX);
476
477 pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
478 pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u);
479 #ifndef TUNNEL_SINK
480 pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u);
481 #endif
482
483 t = pa_tagstruct_new(NULL, 0);
484 pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
485 pa_tagstruct_putu32(t, tag = u->ctag++);
486 pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie));
487 pa_pstream_send_tagstruct(u->pstream, t);
488 pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u);
489
490 }
491
492 #ifdef TUNNEL_SINK
493 static void sink_notify(pa_sink*sink) {
494 struct userdata *u;
495 assert(sink && sink->userdata);
496 u = sink->userdata;
497
498 send_bytes(u);
499 }
500
501 static pa_usec_t sink_get_latency(pa_sink *sink) {
502 struct userdata *u;
503 uint32_t l;
504 pa_usec_t usec = 0;
505 assert(sink && sink->userdata);
506 u = sink->userdata;
507
508 l = DEFAULT_TLENGTH;
509
510 if (l > u->requested_bytes) {
511 l -= u->requested_bytes;
512 usec += pa_bytes_to_usec(l, &u->sink->sample_spec);
513 }
514
515 usec += u->host_latency;
516
517 return usec;
518 }
519 #else
520 static pa_usec_t source_get_latency(pa_source *source) {
521 struct userdata *u;
522 assert(source && source->userdata);
523 u = source->userdata;
524
525 return u->host_latency;
526 }
527 #endif
528
529 static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
530 struct userdata *u = userdata;
531 struct timeval ntv;
532 assert(m && e && u);
533
534 request_latency(u);
535
536 pa_gettimeofday(&ntv);
537 ntv.tv_sec += LATENCY_INTERVAL;
538 m->time_restart(e, &ntv);
539 }
540
541 static int load_key(struct userdata *u, const char*fn) {
542 assert(u);
543
544 u->auth_cookie_in_property = 0;
545
546 if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
547 pa_log_debug(__FILE__": using already loaded auth cookie.\n");
548 pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
549 u->auth_cookie_in_property = 1;
550 return 0;
551 }
552
553 if (!fn)
554 fn = PA_NATIVE_COOKIE_FILE;
555
556 if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
557 return -1;
558
559 pa_log_debug(__FILE__": loading cookie from disk.\n");
560
561 if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
562 u->auth_cookie_in_property = 1;
563
564 return 0;
565 }
566
567 int pa__init(pa_core *c, pa_module*m) {
568 pa_modargs *ma = NULL;
569 struct userdata *u = NULL;
570 pa_sample_spec ss;
571 struct timeval ntv;
572 assert(c && m);
573
574 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
575 pa_log(__FILE__": failed to parse module arguments\n");
576 goto fail;
577 }
578
579 u = pa_xmalloc(sizeof(struct userdata));
580 m->userdata = u;
581 u->module = m;
582 u->core = c;
583 u->client = NULL;
584 u->pdispatch = NULL;
585 u->pstream = NULL;
586 u->server_name = NULL;
587 #ifdef TUNNEL_SINK
588 u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
589 u->sink = NULL;
590 u->requested_bytes = 0;
591 #else
592 u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
593 u->source = NULL;
594 #endif
595 u->ctag = 1;
596 u->device_index = u->channel = PA_INVALID_INDEX;
597 u->host_latency = 0;
598 u->auth_cookie_in_property = 0;
599 u->time_event = NULL;
600
601 if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
602 goto fail;
603
604 if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
605 pa_log(__FILE__": no server specified.\n");
606 goto fail;
607 }
608
609 ss = c->default_sample_spec;
610 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
611 pa_log(__FILE__": invalid sample format specification\n");
612 goto fail;
613 }
614
615 if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
616 pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name);
617 goto fail;
618 }
619
620 if (!u->client)
621 goto fail;
622
623 pa_socket_client_set_callback(u->client, on_connection, u);
624
625 #ifdef TUNNEL_SINK
626 if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
627 pa_log(__FILE__": failed to create sink.\n");
628 goto fail;
629 }
630
631 u->sink->notify = sink_notify;
632 u->sink->get_latency = sink_get_latency;
633 u->sink->userdata = u;
634 u->sink->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->sink_name ? u->sink_name : "", u->sink_name ? "@" : "", u->server_name);
635
636 pa_sink_set_owner(u->sink, m);
637 #else
638 if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) {
639 pa_log(__FILE__": failed to create source.\n");
640 goto fail;
641 }
642
643 u->source->get_latency = source_get_latency;
644 u->source->userdata = u;
645 u->source->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->source_name ? u->source_name : "", u->source_name ? "@" : "", u->server_name);
646
647 pa_source_set_owner(u->source, m);
648 #endif
649
650 pa_gettimeofday(&ntv);
651 ntv.tv_sec += LATENCY_INTERVAL;
652 u->time_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, u);
653
654 pa_modargs_free(ma);
655
656 return 0;
657
658 fail:
659 pa__done(c, m);
660
661 if (ma)
662 pa_modargs_free(ma);
663 return -1;
664 }
665
666 void pa__done(pa_core *c, pa_module*m) {
667 struct userdata* u;
668 assert(c && m);
669
670 if (!(u = m->userdata))
671 return;
672
673 close_stuff(u);
674
675 if (u->auth_cookie_in_property)
676 pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
677
678 #ifdef TUNNEL_SINK
679 pa_xfree(u->sink_name);
680 #else
681 pa_xfree(u->source_name);
682 #endif
683 pa_xfree(u->server_name);
684
685 pa_xfree(u);
686 }
687
688