1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include <pulse/rtclock.h>
29 #include <pulse/timeval.h>
30 #include <pulse/xmalloc.h>
31
32 #include <pulsecore/native-common.h>
33 #include <pulsecore/llist.h>
34 #include <pulsecore/log.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/macro.h>
37 #include <pulsecore/refcnt.h>
38 #include <pulsecore/flist.h>
39 #include <pulsecore/core-rtclock.h>
40
41 #include "pdispatch.h"
42
43 /* #define DEBUG_OPCODES */
44
45 #ifdef DEBUG_OPCODES
46
47 static const char *command_names[PA_COMMAND_MAX] = {
48 /* Generic commands */
49 [PA_COMMAND_ERROR] = "ERROR",
50 [PA_COMMAND_TIMEOUT] = "TIMEOUT",
51 [PA_COMMAND_REPLY] = "REPLY",
52
53 /* CLIENT->SERVER */
54 [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
55 [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
56 [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
57 [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
58 [PA_COMMAND_AUTH] = "AUTH",
59 [PA_COMMAND_EXIT] = "EXIT",
60 [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
61 [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
62 [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
63 [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
64 [PA_COMMAND_STAT] = "STAT",
65 [PA_COMMAND_GET_PLAYBACK_LATENCY] = "GET_PLAYBACK_LATENCY",
66 [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
67 [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
68 [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
69 [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
70 [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
71
72 [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
73 [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
74 [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
75 [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
76 [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
77 [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
78 [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
79 [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
80 [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
81 [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
82 [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
83 [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
84 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
85 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
86 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
87 [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
88
89 [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
90 [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
91 [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLUME",
92
93 [PA_COMMAND_SET_SINK_MUTE] = "SET_SINK_MUTE",
94 [PA_COMMAND_SET_SOURCE_MUTE] = "SET_SOURCE_MUTE",
95
96 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
97 [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
98 [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
99
100 [PA_COMMAND_SET_DEFAULT_SINK] = "SET_DEFAULT_SINK",
101 [PA_COMMAND_SET_DEFAULT_SOURCE] = "SET_DEFAULT_SOURCE",
102
103 [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = "SET_PLAYBACK_STREAM_NAME",
104 [PA_COMMAND_SET_RECORD_STREAM_NAME] = "SET_RECORD_STREAM_NAME",
105
106 [PA_COMMAND_KILL_CLIENT] = "KILL_CLIENT",
107 [PA_COMMAND_KILL_SINK_INPUT] = "KILL_SINK_INPUT",
108 [PA_COMMAND_KILL_SOURCE_OUTPUT] = "KILL_SOURCE_OUTPUT",
109
110 [PA_COMMAND_LOAD_MODULE] = "LOAD_MODULE",
111 [PA_COMMAND_UNLOAD_MODULE] = "UNLOAD_MODULE",
112
113 [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = "ADD_AUTOLOAD (obsolete)",
114 [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = "REMOVE_AUTOLOAD (obsolete)",
115 [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = "GET_AUTOLOAD_INFO (obsolete)",
116 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = "GET_AUTOLOAD_INFO_LIST (obsolete)",
117
118 [PA_COMMAND_GET_RECORD_LATENCY] = "GET_RECORD_LATENCY",
119 [PA_COMMAND_CORK_RECORD_STREAM] = "CORK_RECORD_STREAM",
120 [PA_COMMAND_FLUSH_RECORD_STREAM] = "FLUSH_RECORD_STREAM",
121 [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = "PREBUF_PLAYBACK_STREAM",
122
123 /* SERVER->CLIENT */
124 [PA_COMMAND_REQUEST] = "REQUEST",
125 [PA_COMMAND_OVERFLOW] = "OVERFLOW",
126 [PA_COMMAND_UNDERFLOW] = "UNDERFLOW",
127 [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
128 [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
129 [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
130
131 /* A few more client->server commands */
132
133 /* Supported since protocol v10 (0.9.5) */
134 [PA_COMMAND_MOVE_SINK_INPUT] = "MOVE_SINK_INPUT",
135 [PA_COMMAND_MOVE_SOURCE_OUTPUT] = "MOVE_SOURCE_OUTPUT",
136
137 /* Supported since protocol v11 (0.9.7) */
138 [PA_COMMAND_SET_SINK_INPUT_MUTE] = "SET_SINK_INPUT_MUTE",
139
140 [PA_COMMAND_SUSPEND_SINK] = "SUSPEND_SINK",
141 [PA_COMMAND_SUSPEND_SOURCE] = "SUSPEND_SOURCE",
142
143 /* Supported since protocol v12 (0.9.8) */
144 [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = "SET_PLAYBACK_STREAM_BUFFER_ATTR",
145 [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = "SET_RECORD_STREAM_BUFFER_ATTR",
146
147 [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = "UPDATE_PLAYBACK_STREAM_SAMPLE_RATE",
148 [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = "UPDATE_RECORD_STREAM_SAMPLE_RATE",
149
150 /* SERVER->CLIENT */
151 [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = "PLAYBACK_STREAM_SUSPENDED",
152 [PA_COMMAND_RECORD_STREAM_SUSPENDED] = "RECORD_STREAM_SUSPENDED",
153 [PA_COMMAND_PLAYBACK_STREAM_MOVED] = "PLAYBACK_STREAM_MOVED",
154 [PA_COMMAND_RECORD_STREAM_MOVED] = "RECORD_STREAM_MOVED",
155
156 /* Supported since protocol v13 (0.9.11) */
157 [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = "UPDATE_RECORD_STREAM_PROPLIST",
158 [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = "UPDATE_PLAYBACK_STREAM_PROPLIST",
159 [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = "UPDATE_CLIENT_PROPLIST",
160 [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = "REMOVE_RECORD_STREAM_PROPLIST",
161 [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = "REMOVE_PLAYBACK_STREAM_PROPLIST",
162 [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = "REMOVE_CLIENT_PROPLIST",
163
164 /* SERVER->CLIENT */
165 [PA_COMMAND_STARTED] = "STARTED",
166
167 /* Supported since protocol v14 (0.9.12) */
168 [PA_COMMAND_EXTENSION] = "EXTENSION",
169
170 /* Supported since protocol v15 (0.9.15) */
171 [PA_COMMAND_GET_CARD_INFO] = "GET_CARD_INFO",
172 [PA_COMMAND_GET_CARD_INFO_LIST] = "GET_CARD_INFO_LIST",
173 [PA_COMMAND_SET_CARD_PROFILE] = "SET_CARD_PROFILE",
174
175 [PA_COMMAND_CLIENT_EVENT] = "CLIENT_EVENT",
176 [PA_COMMAND_PLAYBACK_STREAM_EVENT] = "PLAYBACK_STREAM_EVENT",
177 [PA_COMMAND_RECORD_STREAM_EVENT] = "RECORD_STREAM_EVENT",
178
179 /* SERVER->CLIENT */
180 [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = "PLAYBACK_BUFFER_ATTR_CHANGED",
181 [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_BUFFER_ATTR_CHANGED",
182
183 /* Supported since protocol v16 (0.9.16) */
184 [PA_COMMAND_SET_SINK_PORT] = "SET_SINK_PORT",
185 [PA_COMMAND_SET_SOURCE_PORT] = "SET_SOURCE_PORT",
186
187 /* Supported since protocol v22 (1.0) */
188 [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = "SET_SOURCE_OUTPUT_VOLUME",
189 [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = "SET_SOURCE_OUTPUT_MUTE",
190
191 /* Supported since protocol v27 (3.0) */
192 [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = "SET_PORT_LATENCY_OFFSET",
193
194 /* Supported since protocol v30 (6.0) */
195 /* BOTH DIRECTIONS */
196 [PA_COMMAND_ENABLE_SRBCHANNEL] = "ENABLE_SRBCHANNEL",
197 [PA_COMMAND_DISABLE_SRBCHANNEL] = "DISABLE_SRBCHANNEL",
198
199 /* Supported since protocol v31 (9.0) */
200 /* BOTH DIRECTIONS */
201 [PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID",
202
203 /* Supported since protocol v35 (15.0) */
204 [PA_COMMAND_SEND_OBJECT_MESSAGE] = "SEND_OBJECT_MESSAGE",
205 };
206
207 #endif
208
209 PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
210
211 struct reply_info {
212 pa_pdispatch *pdispatch;
213 PA_LLIST_FIELDS(struct reply_info);
214 pa_pdispatch_cb_t callback;
215 void *userdata;
216 pa_free_cb_t free_cb;
217 uint32_t tag;
218 pa_time_event *time_event;
219 };
220
221 struct pa_pdispatch {
222 PA_REFCNT_DECLARE;
223 pa_mainloop_api *mainloop;
224 const pa_pdispatch_cb_t *callback_table;
225 unsigned n_commands;
226 PA_LLIST_HEAD(struct reply_info, replies);
227 pa_pdispatch_drain_cb_t drain_callback;
228 void *drain_userdata;
229 pa_cmsg_ancil_data *ancil_data;
230 bool use_rtclock;
231 };
232
reply_info_free(struct reply_info * r)233 static void reply_info_free(struct reply_info *r) {
234 pa_assert(r);
235 pa_assert(r->pdispatch);
236 pa_assert(r->pdispatch->mainloop);
237
238 if (r->time_event)
239 r->pdispatch->mainloop->time_free(r->time_event);
240
241 PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
242
243 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
244 pa_xfree(r);
245 }
246
pa_pdispatch_new(pa_mainloop_api * mainloop,bool use_rtclock,const pa_pdispatch_cb_t * table,unsigned entries)247 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, bool use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries) {
248 pa_pdispatch *pd;
249
250 pa_assert(mainloop);
251 pa_assert((entries && table) || (!entries && !table));
252
253 pd = pa_xnew0(pa_pdispatch, 1);
254 PA_REFCNT_INIT(pd);
255 pd->mainloop = mainloop;
256 pd->callback_table = table;
257 pd->n_commands = entries;
258 PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
259 pd->use_rtclock = use_rtclock;
260
261 return pd;
262 }
263
pdispatch_free(pa_pdispatch * pd)264 static void pdispatch_free(pa_pdispatch *pd) {
265 pa_assert(pd);
266
267 while (pd->replies) {
268 if (pd->replies->free_cb)
269 pd->replies->free_cb(pd->replies->userdata);
270
271 reply_info_free(pd->replies);
272 }
273
274 pa_xfree(pd);
275 }
276
run_action(pa_pdispatch * pd,struct reply_info * r,uint32_t command,pa_tagstruct * ts)277 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
278 pa_pdispatch_cb_t callback;
279 void *userdata;
280 uint32_t tag;
281 pa_assert(r);
282
283 pa_pdispatch_ref(pd);
284
285 callback = r->callback;
286 userdata = r->userdata;
287 tag = r->tag;
288
289 reply_info_free(r);
290
291 callback(pd, command, tag, ts, userdata);
292
293 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
294 pd->drain_callback(pd, pd->drain_userdata);
295
296 pa_pdispatch_unref(pd);
297 }
298
pa_pdispatch_run(pa_pdispatch * pd,pa_packet * packet,pa_cmsg_ancil_data * ancil_data,void * userdata)299 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
300 uint32_t tag, command;
301 pa_tagstruct *ts = NULL;
302 int ret = -1;
303 const void *pdata;
304 size_t plen;
305
306 pa_assert(pd);
307 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
308 pa_assert(packet);
309
310 pa_pdispatch_ref(pd);
311
312 pdata = pa_packet_data(packet, &plen);
313 if (plen <= 8)
314 goto finish;
315
316 ts = pa_tagstruct_new_fixed(pdata, plen);
317
318 if (pa_tagstruct_getu32(ts, &command) < 0 ||
319 pa_tagstruct_getu32(ts, &tag) < 0)
320 goto finish;
321
322 #ifdef DEBUG_OPCODES
323 {
324 char t[256];
325 char const *p = NULL;
326
327 if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
328 pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
329
330 pa_log("[%p] Received opcode <%s>", pd, p);
331 }
332 #endif
333
334 pd->ancil_data = ancil_data;
335
336 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
337 struct reply_info *r;
338
339 PA_LLIST_FOREACH(r, pd->replies)
340 if (r->tag == tag)
341 break;
342
343 if (r)
344 run_action(pd, r, command, ts);
345
346 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
347 const pa_pdispatch_cb_t *cb = pd->callback_table+command;
348
349 (*cb)(pd, command, tag, ts, userdata);
350 } else {
351 pa_log("Received unsupported command %u", command);
352 goto finish;
353 }
354
355 ret = 0;
356
357 finish:
358 pd->ancil_data = NULL;
359
360 if (ts)
361 pa_tagstruct_free(ts);
362
363 pa_pdispatch_unref(pd);
364
365 return ret;
366 }
367
timeout_callback(pa_mainloop_api * m,pa_time_event * e,const struct timeval * t,void * userdata)368 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *t, void *userdata) {
369 struct reply_info*r = userdata;
370
371 pa_assert(r);
372 pa_assert(r->time_event == e);
373 pa_assert(r->pdispatch);
374 pa_assert(r->pdispatch->mainloop == m);
375 pa_assert(r->callback);
376
377 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
378 }
379
pa_pdispatch_register_reply(pa_pdispatch * pd,uint32_t tag,int timeout,pa_pdispatch_cb_t cb,void * userdata,pa_free_cb_t free_cb)380 void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
381 struct reply_info *r;
382 struct timeval tv;
383
384 pa_assert(pd);
385 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
386 pa_assert(cb);
387
388 if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
389 r = pa_xnew(struct reply_info, 1);
390
391 r->pdispatch = pd;
392 r->callback = cb;
393 r->userdata = userdata;
394 r->free_cb = free_cb;
395 r->tag = tag;
396
397 pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop,
398 pa_timeval_rtstore(&tv, pa_rtclock_now() + timeout * PA_USEC_PER_SEC, pd->use_rtclock),
399 timeout_callback, r));
400
401 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
402 }
403
pa_pdispatch_is_pending(pa_pdispatch * pd)404 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
405 pa_assert(pd);
406 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
407
408 return !!pd->replies;
409 }
410
pa_pdispatch_set_drain_callback(pa_pdispatch * pd,pa_pdispatch_drain_cb_t cb,void * userdata)411 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_cb_t cb, void *userdata) {
412 pa_assert(pd);
413 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
414 pa_assert(!cb || pa_pdispatch_is_pending(pd));
415
416 pd->drain_callback = cb;
417 pd->drain_userdata = userdata;
418 }
419
pa_pdispatch_unregister_reply(pa_pdispatch * pd,void * userdata)420 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
421 struct reply_info *r, *n;
422
423 pa_assert(pd);
424 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
425
426 PA_LLIST_FOREACH_SAFE(r, n, pd->replies)
427 if (r->userdata == userdata)
428 reply_info_free(r);
429 }
430
pa_pdispatch_unref(pa_pdispatch * pd)431 void pa_pdispatch_unref(pa_pdispatch *pd) {
432 pa_assert(pd);
433 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
434
435 if (PA_REFCNT_DEC(pd) <= 0)
436 pdispatch_free(pd);
437 }
438
pa_pdispatch_ref(pa_pdispatch * pd)439 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
440 pa_assert(pd);
441 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
442
443 PA_REFCNT_INC(pd);
444 return pd;
445 }
446
447 #ifdef HAVE_CREDS
448
pa_pdispatch_creds(pa_pdispatch * pd)449 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
450 pa_assert(pd);
451 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
452
453 if (pd->ancil_data && pd->ancil_data->creds_valid)
454 return &pd->ancil_data->creds;
455 return NULL;
456 }
457
458 /* Should be called only once during the dispatcher lifetime
459 *
460 * If the returned ancillary data contains any fds, caller maintains sole
461 * responsibility of closing them down using pa_cmsg_ancil_data_close_fds() */
pa_pdispatch_take_ancil_data(pa_pdispatch * pd)462 pa_cmsg_ancil_data *pa_pdispatch_take_ancil_data(pa_pdispatch *pd) {
463 pa_cmsg_ancil_data *ancil;
464
465 pa_assert(pd);
466 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
467
468 ancil = pd->ancil_data;
469
470 /* iochannel guarantees us that nfd will always be capped */
471 if (ancil)
472 pa_assert(ancil->nfd <= MAX_ANCIL_DATA_FDS);
473
474 pd->ancil_data = NULL;
475 return ancil;
476 }
477
478 #endif
479