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