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
204 #endif
205
206 PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
207
208 struct reply_info {
209 pa_pdispatch *pdispatch;
210 PA_LLIST_FIELDS(struct reply_info);
211 pa_pdispatch_cb_t callback;
212 void *userdata;
213 pa_free_cb_t free_cb;
214 uint32_t tag;
215 pa_time_event *time_event;
216 };
217
218 struct pa_pdispatch {
219 PA_REFCNT_DECLARE;
220 pa_mainloop_api *mainloop;
221 const pa_pdispatch_cb_t *callback_table;
222 unsigned n_commands;
223 PA_LLIST_HEAD(struct reply_info, replies);
224 pa_pdispatch_drain_cb_t drain_callback;
225 void *drain_userdata;
226 pa_cmsg_ancil_data *ancil_data;
227 bool use_rtclock;
228 };
229
reply_info_free(struct reply_info * r)230 static void reply_info_free(struct reply_info *r) {
231 pa_assert(r);
232 pa_assert(r->pdispatch);
233 pa_assert(r->pdispatch->mainloop);
234
235 if (r->time_event)
236 r->pdispatch->mainloop->time_free(r->time_event);
237
238 PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
239
240 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
241 pa_xfree(r);
242 }
243
pa_pdispatch_new(pa_mainloop_api * mainloop,bool use_rtclock,const pa_pdispatch_cb_t * table,unsigned entries)244 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, bool use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries) {
245 pa_pdispatch *pd;
246
247 pa_assert(mainloop);
248 pa_assert((entries && table) || (!entries && !table));
249
250 pd = pa_xnew0(pa_pdispatch, 1);
251 PA_REFCNT_INIT(pd);
252 pd->mainloop = mainloop;
253 pd->callback_table = table;
254 pd->n_commands = entries;
255 PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
256 pd->use_rtclock = use_rtclock;
257
258 return pd;
259 }
260
pdispatch_free(pa_pdispatch * pd)261 static void pdispatch_free(pa_pdispatch *pd) {
262 pa_assert(pd);
263
264 while (pd->replies) {
265 if (pd->replies->free_cb)
266 pd->replies->free_cb(pd->replies->userdata);
267
268 reply_info_free(pd->replies);
269 }
270
271 pa_xfree(pd);
272 }
273
run_action(pa_pdispatch * pd,struct reply_info * r,uint32_t command,pa_tagstruct * ts)274 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
275 pa_pdispatch_cb_t callback;
276 void *userdata;
277 uint32_t tag;
278 pa_assert(r);
279
280 pa_pdispatch_ref(pd);
281
282 callback = r->callback;
283 userdata = r->userdata;
284 tag = r->tag;
285
286 reply_info_free(r);
287
288 callback(pd, command, tag, ts, userdata);
289
290 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
291 pd->drain_callback(pd, pd->drain_userdata);
292
293 pa_pdispatch_unref(pd);
294 }
295
pa_pdispatch_run(pa_pdispatch * pd,pa_packet * packet,pa_cmsg_ancil_data * ancil_data,void * userdata)296 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
297 uint32_t tag, command;
298 pa_tagstruct *ts = NULL;
299 int ret = -1;
300 const void *pdata;
301 size_t plen;
302
303 pa_assert(pd);
304 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
305 pa_assert(packet);
306
307 pa_pdispatch_ref(pd);
308
309 pdata = pa_packet_data(packet, &plen);
310 if (plen <= 8)
311 goto finish;
312
313 ts = pa_tagstruct_new_fixed(pdata, plen);
314
315 if (pa_tagstruct_getu32(ts, &command) < 0 ||
316 pa_tagstruct_getu32(ts, &tag) < 0)
317 goto finish;
318
319 #ifdef DEBUG_OPCODES
320 {
321 char t[256];
322 char const *p = NULL;
323
324 if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
325 pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
326
327 pa_log("[%p] Received opcode <%s>", pd, p);
328 }
329 #endif
330
331 pd->ancil_data = ancil_data;
332
333 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
334 struct reply_info *r;
335
336 PA_LLIST_FOREACH(r, pd->replies)
337 if (r->tag == tag)
338 break;
339
340 if (r)
341 run_action(pd, r, command, ts);
342
343 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
344 const pa_pdispatch_cb_t *cb = pd->callback_table+command;
345
346 (*cb)(pd, command, tag, ts, userdata);
347 } else {
348 pa_log("Received unsupported command %u", command);
349 goto finish;
350 }
351
352 ret = 0;
353
354 finish:
355 pd->ancil_data = NULL;
356
357 if (ts)
358 pa_tagstruct_free(ts);
359
360 pa_pdispatch_unref(pd);
361
362 return ret;
363 }
364
timeout_callback(pa_mainloop_api * m,pa_time_event * e,const struct timeval * t,void * userdata)365 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *t, void *userdata) {
366 struct reply_info*r = userdata;
367
368 pa_assert(r);
369 pa_assert(r->time_event == e);
370 pa_assert(r->pdispatch);
371 pa_assert(r->pdispatch->mainloop == m);
372 pa_assert(r->callback);
373
374 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
375 }
376
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)377 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) {
378 struct reply_info *r;
379 struct timeval tv;
380
381 pa_assert(pd);
382 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
383 pa_assert(cb);
384
385 if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
386 r = pa_xnew(struct reply_info, 1);
387
388 r->pdispatch = pd;
389 r->callback = cb;
390 r->userdata = userdata;
391 r->free_cb = free_cb;
392 r->tag = tag;
393
394 pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop,
395 pa_timeval_rtstore(&tv, pa_rtclock_now() + timeout * PA_USEC_PER_SEC, pd->use_rtclock),
396 timeout_callback, r));
397
398 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
399 }
400
pa_pdispatch_is_pending(pa_pdispatch * pd)401 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
402 pa_assert(pd);
403 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
404
405 return !!pd->replies;
406 }
407
pa_pdispatch_set_drain_callback(pa_pdispatch * pd,pa_pdispatch_drain_cb_t cb,void * userdata)408 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_cb_t cb, void *userdata) {
409 pa_assert(pd);
410 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
411 pa_assert(!cb || pa_pdispatch_is_pending(pd));
412
413 pd->drain_callback = cb;
414 pd->drain_userdata = userdata;
415 }
416
pa_pdispatch_unregister_reply(pa_pdispatch * pd,void * userdata)417 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
418 struct reply_info *r, *n;
419
420 pa_assert(pd);
421 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
422
423 PA_LLIST_FOREACH_SAFE(r, n, pd->replies)
424 if (r->userdata == userdata)
425 reply_info_free(r);
426 }
427
pa_pdispatch_unref(pa_pdispatch * pd)428 void pa_pdispatch_unref(pa_pdispatch *pd) {
429 pa_assert(pd);
430 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
431
432 if (PA_REFCNT_DEC(pd) <= 0)
433 pdispatch_free(pd);
434 }
435
pa_pdispatch_ref(pa_pdispatch * pd)436 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
437 pa_assert(pd);
438 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
439
440 PA_REFCNT_INC(pd);
441 return pd;
442 }
443
444 #ifdef HAVE_CREDS
445
pa_pdispatch_creds(pa_pdispatch * pd)446 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
447 pa_assert(pd);
448 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
449
450 if (pd->ancil_data && pd->ancil_data->creds_valid)
451 return &pd->ancil_data->creds;
452 return NULL;
453 }
454
455 /* Should be called only once during the dispatcher lifetime
456 *
457 * If the returned ancillary data contains any fds, caller maintains sole
458 * responsibility of closing them down using pa_cmsg_ancil_data_close_fds() */
pa_pdispatch_take_ancil_data(pa_pdispatch * pd)459 pa_cmsg_ancil_data *pa_pdispatch_take_ancil_data(pa_pdispatch *pd) {
460 pa_cmsg_ancil_data *ancil;
461
462 pa_assert(pd);
463 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
464
465 ancil = pd->ancil_data;
466
467 /* iochannel guarantees us that nfd will always be capped */
468 if (ancil)
469 pa_assert(ancil->nfd <= MAX_ANCIL_DATA_FDS);
470
471 pd->ancil_data = NULL;
472 return ancil;
473 }
474
475 #endif
476