• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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