summaryrefslogtreecommitdiff
path: root/src/pulsecore/pstream-util.c
blob: 449ea1f851809220f5454c2d9e051849ef246f2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/***
  This file is part of PulseAudio.

  Copyright 2004-2006 Lennart Poettering

  PulseAudio is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as
  published by the Free Software Foundation; either version 2.1 of the
  License, or (at your option) any later version.

  PulseAudio is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/native-common.h>
#include <pulsecore/pstream.h>
#include <pulsecore/refcnt.h>
#include <pulse/xmalloc.h>

#include "pstream-util.h"

static void pa_pstream_send_tagstruct_with_ancil_data(pa_pstream *p, pa_tagstruct *t, pa_cmsg_ancil_data *ancil_data) {
    size_t length;
    const uint8_t *data;
    pa_packet *packet;

    pa_assert(p);
    pa_assert(t);

    pa_assert_se(data = pa_tagstruct_data(t, &length));
    pa_assert_se(packet = pa_packet_new_data(data, length));
    pa_tagstruct_free(t);

    pa_pstream_send_packet(p, packet, ancil_data);
    pa_packet_unref(packet);
}

#ifdef HAVE_CREDS

void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds) {
    if (creds) {
        pa_cmsg_ancil_data a;

        a.nfd = 0;
        a.creds_valid = true;
        a.creds = *creds;
        pa_pstream_send_tagstruct_with_ancil_data(p, t, &a);
    }
    else
        pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
}

/* @close_fds: If set then the pstreams code, after invoking a sendmsg(),
 * will close all passed fds.
 *
 * Such fds cannot be closed here as this might lead to freeing them
 * before they're actually passed to the other end. The internally-used
 * pa_pstream_send_packet() does not do any actual writes and just
 * defers write events over the pstream. */
void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds,
                                        bool close_fds) {
    if (nfd > 0) {
        pa_cmsg_ancil_data a;

        a.nfd = nfd;
        a.creds_valid = false;
        a.close_fds_on_cleanup = close_fds;
        pa_assert(nfd <= MAX_ANCIL_DATA_FDS);
        memcpy(a.fds, fds, sizeof(int) * nfd);
        pa_pstream_send_tagstruct_with_ancil_data(p, t, &a);
    }
    else
        pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
}

#else

void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds) {
    pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
}

void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds,
                                        bool close_fds) {
    pa_assert_not_reached();
}

#endif

void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
    pa_tagstruct *t;

    pa_assert_se(t = pa_tagstruct_new());
    pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
    pa_tagstruct_putu32(t, tag);
    pa_tagstruct_putu32(t, error);
    pa_pstream_send_tagstruct(p, t);
}

void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
    pa_tagstruct *t;

    pa_assert_se(t = pa_tagstruct_new());
    pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
    pa_tagstruct_putu32(t, tag);
    pa_pstream_send_tagstruct(p, t);
}

/* Before sending blocks from a memfd-backed pool over the pipe, we
 * must call this method first.
 *
 * This is needed to transfer memfd blocks without passing their fd
 * every time, thus minimizing overhead and avoiding fd leaks.
 *
 * On registration a packet is sent with the memfd fd as ancil data;
 * such packet has an ID that uniquely identifies the pool's memfd
 * region. Upon arrival the other end creates a permanent mapping
 * between that ID and the passed memfd memory area.
 *
 * By doing so, we won't need to reference the pool's memfd fd any
 * further - just its ID. Both endpoints can then close their fds. */
int pa_pstream_register_memfd_mempool(pa_pstream *p, pa_mempool *pool, const char **fail_reason) {
#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
    unsigned shm_id;
    int memfd_fd, ret = -1;
    pa_tagstruct *t;
    bool per_client_mempool;

    pa_assert(p);
    pa_assert(fail_reason);

    *fail_reason = NULL;
    per_client_mempool = pa_mempool_is_per_client(pool);

    pa_pstream_ref(p);

    if (!pa_mempool_is_shared(pool)) {
        *fail_reason = "mempool is not shared";
        goto finish;
    }

    if (!pa_mempool_is_memfd_backed(pool)) {
        *fail_reason = "mempool is not memfd-backed";
        goto finish;
    }

    if (pa_mempool_get_shm_id(pool, &shm_id)) {
        *fail_reason = "could not extract pool SHM ID";
        goto finish;
    }

    if (!pa_pstream_get_memfd(p)) {
        *fail_reason = "pipe does not support memfd transport";
        goto finish;
    }

    memfd_fd = (per_client_mempool) ? pa_mempool_take_memfd_fd(pool) :
                                      pa_mempool_get_memfd_fd(pool);

    /* Note! For per-client mempools we've taken ownership of the memfd
     * fd, and we're thus the sole code path responsible for closing it.
     * In case of any failure, it MUST be closed. */

    if (pa_pstream_attach_memfd_shmid(p, shm_id, memfd_fd)) {
        *fail_reason = "could not attach memfd SHM ID to pipe";

        if (per_client_mempool)
            pa_assert_se(pa_close(memfd_fd) == 0);
        goto finish;
    }

    t = pa_tagstruct_new();
    pa_tagstruct_putu32(t, PA_COMMAND_REGISTER_MEMFD_SHMID);
    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
    pa_tagstruct_putu32(t, shm_id);
    pa_pstream_send_tagstruct_with_fds(p, t, 1, &memfd_fd, per_client_mempool);

    ret = 0;
finish:
    pa_pstream_unref(p);
    return ret;

#else
    pa_assert_not_reached();
#endif
}