• 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     /* 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