mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
add support for authentication using SCM_CREDENTIALS
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@596 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
a1f5573313
commit
3f264b2c4a
12 changed files with 343 additions and 92 deletions
|
|
@ -27,12 +27,14 @@
|
|||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "winsock.h"
|
||||
|
||||
#include <polypcore/util.h>
|
||||
#include <polypcore/socket-util.h>
|
||||
#include <polypcore/xmalloc.h>
|
||||
#include <polypcore/log.h>
|
||||
|
||||
#include "iochannel.h"
|
||||
|
||||
|
|
@ -242,6 +244,134 @@ ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
|
|||
return r;
|
||||
}
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
|
||||
int pa_iochannel_creds_supported(pa_iochannel *io) {
|
||||
struct sockaddr_un sa;
|
||||
socklen_t l;
|
||||
|
||||
assert(io);
|
||||
assert(io->ifd >= 0);
|
||||
assert(io->ofd == io->ifd);
|
||||
|
||||
l = sizeof(sa);
|
||||
|
||||
if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0)
|
||||
return 0;
|
||||
|
||||
return sa.sun_family == AF_UNIX;
|
||||
}
|
||||
|
||||
int pa_iochannel_creds_enable(pa_iochannel *io) {
|
||||
int t = 1;
|
||||
|
||||
assert(io);
|
||||
assert(io->ifd >= 0);
|
||||
|
||||
if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
|
||||
pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l) {
|
||||
ssize_t r;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
|
||||
struct ucred *ucred;
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
assert(io);
|
||||
assert(data);
|
||||
assert(l);
|
||||
assert(io->ofd >= 0);
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
iov.iov_base = (void*) data;
|
||||
iov.iov_len = l;
|
||||
|
||||
memset(cmsg_data, 0, sizeof(cmsg_data));
|
||||
cmsg = (struct cmsghdr*) cmsg_data;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_CREDENTIALS;
|
||||
|
||||
ucred = (struct ucred*) CMSG_DATA(cmsg);
|
||||
ucred->pid = getpid();
|
||||
ucred->uid = getuid();
|
||||
ucred->gid = getgid();
|
||||
|
||||
memset(&mh, 0, sizeof(mh));
|
||||
mh.msg_name = NULL;
|
||||
mh.msg_namelen = 0;
|
||||
mh.msg_iov = &iov;
|
||||
mh.msg_iovlen = 1;
|
||||
mh.msg_control = cmsg_data;
|
||||
mh.msg_controllen = sizeof(cmsg_data);
|
||||
mh.msg_flags = 0;
|
||||
|
||||
if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
|
||||
io->writable = 0;
|
||||
enable_mainloop_sources(io);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid) {
|
||||
ssize_t r;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
|
||||
|
||||
assert(io);
|
||||
assert(data);
|
||||
assert(l);
|
||||
assert(io->ifd >= 0);
|
||||
assert(ucred);
|
||||
assert(creds_valid);
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
iov.iov_base = data;
|
||||
iov.iov_len = l;
|
||||
|
||||
memset(cmsg_data, 0, sizeof(cmsg_data));
|
||||
|
||||
memset(&mh, 0, sizeof(mh));
|
||||
mh.msg_name = NULL;
|
||||
mh.msg_namelen = 0;
|
||||
mh.msg_iov = &iov;
|
||||
mh.msg_iovlen = 1;
|
||||
mh.msg_control = cmsg_data;
|
||||
mh.msg_controllen = sizeof(cmsg_data);
|
||||
mh.msg_flags = 0;
|
||||
|
||||
if ((r = recvmsg(io->ifd, &mh, MSG_NOSIGNAL)) >= 0) {
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
*creds_valid = 0;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
|
||||
|
||||
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
|
||||
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
|
||||
memcpy(ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
|
||||
*creds_valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
io->readable = 0;
|
||||
enable_mainloop_sources(io);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
|
||||
assert(io);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
***/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <polyp/mainloop-api.h>
|
||||
|
||||
/* A wrapper around UNIX file descriptors for attaching them to the a
|
||||
|
|
@ -48,6 +51,14 @@ void pa_iochannel_free(pa_iochannel*io);
|
|||
ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
|
||||
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
int pa_iochannel_creds_supported(pa_iochannel *io);
|
||||
int pa_iochannel_creds_enable(pa_iochannel *io);
|
||||
|
||||
ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l);
|
||||
ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid);
|
||||
#endif
|
||||
|
||||
int pa_iochannel_is_readable(pa_iochannel*io);
|
||||
int pa_iochannel_is_writable(pa_iochannel*io);
|
||||
int pa_iochannel_is_hungup(pa_iochannel*io);
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ struct pa_pdispatch {
|
|||
PA_LLIST_HEAD(struct reply_info, replies);
|
||||
pa_pdispatch_drain_callback drain_callback;
|
||||
void *drain_userdata;
|
||||
const void *creds;
|
||||
};
|
||||
|
||||
static void reply_info_free(struct reply_info *r) {
|
||||
|
|
@ -136,7 +137,8 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_
|
|||
PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies);
|
||||
pd->drain_callback = NULL;
|
||||
pd->drain_userdata = NULL;
|
||||
|
||||
pd->creds = NULL;
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +173,7 @@ static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command,
|
|||
pa_pdispatch_unref(pd);
|
||||
}
|
||||
|
||||
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) {
|
||||
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const void *creds, void *userdata) {
|
||||
uint32_t tag, command;
|
||||
pa_tagstruct *ts = NULL;
|
||||
int ret = -1;
|
||||
|
|
@ -188,18 +190,20 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) {
|
|||
if (pa_tagstruct_getu32(ts, &command) < 0 ||
|
||||
pa_tagstruct_getu32(ts, &tag) < 0)
|
||||
goto finish;
|
||||
|
||||
|
||||
#ifdef DEBUG_OPCODES
|
||||
{
|
||||
char t[256];
|
||||
char const *p;
|
||||
if (!(p = command_names[command]))
|
||||
snprintf((char*) (p = t), sizeof(t), "%u", command);
|
||||
|
||||
|
||||
pa_log(__FILE__": Recieved opcode <%s>", p);
|
||||
}
|
||||
#endif
|
||||
|
||||
pd->creds = creds;
|
||||
|
||||
if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
|
||||
struct reply_info *r;
|
||||
|
||||
|
|
@ -222,6 +226,8 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) {
|
|||
ret = 0;
|
||||
|
||||
finish:
|
||||
pd->creds = NULL;
|
||||
|
||||
if (ts)
|
||||
pa_tagstruct_free(ts);
|
||||
|
||||
|
|
@ -295,3 +301,10 @@ pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
|
|||
pd->ref++;
|
||||
return pd;
|
||||
}
|
||||
|
||||
const void * pa_pdispatch_creds(pa_pdispatch *pd) {
|
||||
assert(pd);
|
||||
assert(pd->ref >= 1);
|
||||
|
||||
return pd->creds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,21 +30,23 @@
|
|||
typedef struct pa_pdispatch pa_pdispatch;
|
||||
|
||||
typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
||||
typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
|
||||
|
||||
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_cb_t*table, unsigned entries);
|
||||
void pa_pdispatch_unref(pa_pdispatch *pd);
|
||||
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
|
||||
|
||||
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, void *userdata);
|
||||
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, const void*creds, void *userdata);
|
||||
|
||||
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t callback, void *userdata);
|
||||
|
||||
int pa_pdispatch_is_pending(pa_pdispatch *pd);
|
||||
|
||||
typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
|
||||
void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata);
|
||||
|
||||
/* Remove all reply slots with the give userdata parameter */
|
||||
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
|
||||
|
||||
const void * pa_pdispatch_creds(pa_pdispatch *pd);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <polypcore/native-common.h>
|
||||
#include <polypcore/packet.h>
|
||||
|
|
@ -357,7 +358,7 @@ static struct playback_stream* playback_stream_new(
|
|||
pa_cvolume *volume,
|
||||
uint32_t syncid) {
|
||||
|
||||
struct playback_stream *s, *sync;
|
||||
struct playback_stream *s, *ssync;
|
||||
pa_sink_input *sink_input;
|
||||
pa_memblock *silence;
|
||||
uint32_t idx;
|
||||
|
|
@ -366,17 +367,17 @@ static struct playback_stream* playback_stream_new(
|
|||
assert(c && sink && ss && name && maxlength);
|
||||
|
||||
/* Find syncid group */
|
||||
for (sync = pa_idxset_first(c->output_streams, &idx); sync; sync = pa_idxset_next(c->output_streams, &idx)) {
|
||||
for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
|
||||
|
||||
if (sync->type != PLAYBACK_STREAM)
|
||||
if (ssync->type != PLAYBACK_STREAM)
|
||||
continue;
|
||||
|
||||
if (sync->syncid == syncid)
|
||||
if (ssync->syncid == syncid)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Synced streams must connect to the same sink */
|
||||
if (sync && sync->sink_input->sink != sink)
|
||||
if (ssync && ssync->sink_input->sink != sink)
|
||||
return NULL;
|
||||
|
||||
if (!(sink_input = pa_sink_input_new(sink, __FILE__, name, ss, map, 0, -1)))
|
||||
|
|
@ -397,16 +398,16 @@ static struct playback_stream* playback_stream_new(
|
|||
s->sink_input->owner = c->protocol->module;
|
||||
s->sink_input->client = c->client;
|
||||
|
||||
if (sync) {
|
||||
if (ssync) {
|
||||
/* Sync id found, now find head of list */
|
||||
PA_LLIST_FIND_HEAD(struct playback_stream, sync, &sync);
|
||||
PA_LLIST_FIND_HEAD(struct playback_stream, ssync, &ssync);
|
||||
|
||||
/* Prepend ourselves */
|
||||
PA_LLIST_PREPEND(struct playback_stream, sync, s);
|
||||
PA_LLIST_PREPEND(struct playback_stream, ssync, s);
|
||||
|
||||
/* Set our start index to the current read index of the other grozp member(s) */
|
||||
assert(sync->next);
|
||||
start_index = pa_memblockq_get_read_index(sync->next->memblockq);
|
||||
assert(ssync->next);
|
||||
start_index = pa_memblockq_get_read_index(ssync->next->memblockq);
|
||||
} else {
|
||||
/* This ia a new sync group */
|
||||
PA_LLIST_INIT(struct playback_stream, s);
|
||||
|
|
@ -871,8 +872,29 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
|
|||
}
|
||||
|
||||
if (!c->authorized) {
|
||||
if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) {
|
||||
pa_log(__FILE__": Denied access to client with invalid authorization key.");
|
||||
int success = 0;
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
const struct ucred *ucred = pa_pdispatch_creds(pd);
|
||||
|
||||
if (ucred) {
|
||||
if (ucred->uid == getuid())
|
||||
success = 1;
|
||||
|
||||
pa_log_info(__FILE__": Got credentials: pid=%lu uid=%lu gid=%lu auth=%i",
|
||||
(unsigned long) ucred->pid,
|
||||
(unsigned long) ucred->uid,
|
||||
(unsigned long) ucred->gid,
|
||||
success);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
|
||||
success = 1;
|
||||
|
||||
if (!success) {
|
||||
pa_log_warn(__FILE__": Denied access to client with invalid authorization data.");
|
||||
pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1589,7 +1611,7 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
|
|||
struct connection *c = userdata;
|
||||
uint32_t idx;
|
||||
int b;
|
||||
struct playback_stream *s, *sync;
|
||||
struct playback_stream *s, *ssync;
|
||||
assert(c && t);
|
||||
|
||||
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
||||
|
|
@ -1609,14 +1631,14 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
|
|||
pa_memblockq_prebuf_force(s->memblockq);
|
||||
|
||||
/* Do the same for all other members in the sync group */
|
||||
for (sync = s->prev; sync; sync = sync->prev) {
|
||||
pa_sink_input_cork(sync->sink_input, b);
|
||||
pa_memblockq_prebuf_force(sync->memblockq);
|
||||
for (ssync = s->prev; ssync; ssync = ssync->prev) {
|
||||
pa_sink_input_cork(ssync->sink_input, b);
|
||||
pa_memblockq_prebuf_force(ssync->memblockq);
|
||||
}
|
||||
|
||||
for (sync = s->next; sync; sync = sync->next) {
|
||||
pa_sink_input_cork(sync->sink_input, b);
|
||||
pa_memblockq_prebuf_force(sync->memblockq);
|
||||
for (ssync = s->next; ssync; ssync = ssync->next) {
|
||||
pa_sink_input_cork(ssync->sink_input, b);
|
||||
pa_memblockq_prebuf_force(ssync->memblockq);
|
||||
}
|
||||
|
||||
pa_pstream_send_simple_ack(c->pstream, tag);
|
||||
|
|
@ -1625,7 +1647,7 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
|
|||
static void command_flush_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
struct connection *c = userdata;
|
||||
uint32_t idx;
|
||||
struct playback_stream *s, *sync;
|
||||
struct playback_stream *s, *ssync;
|
||||
assert(c && t);
|
||||
|
||||
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
||||
|
|
@ -1644,25 +1666,25 @@ static void command_flush_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC
|
|||
s->underrun = 0;
|
||||
|
||||
/* Do the same for all other members in the sync group */
|
||||
for (sync = s->prev; sync; sync = sync->prev) {
|
||||
pa_memblockq_flush(sync->memblockq);
|
||||
sync->underrun = 0;
|
||||
for (ssync = s->prev; ssync; ssync = ssync->prev) {
|
||||
pa_memblockq_flush(ssync->memblockq);
|
||||
ssync->underrun = 0;
|
||||
}
|
||||
|
||||
for (sync = s->next; sync; sync = sync->next) {
|
||||
pa_memblockq_flush(sync->memblockq);
|
||||
sync->underrun = 0;
|
||||
for (ssync = s->next; ssync; ssync = ssync->next) {
|
||||
pa_memblockq_flush(ssync->memblockq);
|
||||
ssync->underrun = 0;
|
||||
}
|
||||
|
||||
pa_pstream_send_simple_ack(c->pstream, tag);
|
||||
pa_sink_notify(s->sink_input->sink);
|
||||
request_bytes(s);
|
||||
|
||||
for (sync = s->prev; sync; sync = sync->prev)
|
||||
request_bytes(sync);
|
||||
for (ssync = s->prev; ssync; ssync = ssync->prev)
|
||||
request_bytes(ssync);
|
||||
|
||||
for (sync = s->next; sync; sync = sync->next)
|
||||
request_bytes(sync);
|
||||
for (ssync = s->next; ssync; ssync = ssync->next)
|
||||
request_bytes(ssync);
|
||||
}
|
||||
|
||||
static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
|
|
@ -2017,11 +2039,11 @@ static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
|
|||
|
||||
/*** pstream callbacks ***/
|
||||
|
||||
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) {
|
||||
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata) {
|
||||
struct connection *c = userdata;
|
||||
assert(p && packet && packet->data && c);
|
||||
|
||||
if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
|
||||
if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) {
|
||||
pa_log(__FILE__": invalid packet.");
|
||||
connection_free(c);
|
||||
}
|
||||
|
|
@ -2183,6 +2205,13 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
|
|||
c->subscription = NULL;
|
||||
|
||||
pa_idxset_put(p->connections, c, NULL);
|
||||
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
if (pa_iochannel_creds_supported(io))
|
||||
pa_iochannel_creds_enable(io);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/*** module entry points ***/
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include "pstream-util.h"
|
||||
|
||||
void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) {
|
||||
void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, int creds) {
|
||||
size_t length;
|
||||
uint8_t *data;
|
||||
pa_packet *packet;
|
||||
|
|
@ -40,7 +40,7 @@ void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) {
|
|||
assert(data && length);
|
||||
packet = pa_packet_new_dynamic(data, length);
|
||||
assert(packet);
|
||||
pa_pstream_send_packet(p, packet);
|
||||
pa_pstream_send_packet(p, packet, creds);
|
||||
pa_packet_unref(packet);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@
|
|||
#include <polypcore/tagstruct.h>
|
||||
|
||||
/* The tagstruct is freed!*/
|
||||
void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t);
|
||||
void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, int creds);
|
||||
|
||||
#define pa_pstream_send_tagstruct(p, t) pa_pstream_send_tagstruct_with_creds((p), (t), 0)
|
||||
|
||||
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error);
|
||||
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ struct item_info {
|
|||
|
||||
/* packet info */
|
||||
pa_packet *packet;
|
||||
#ifdef SCM_CREDENTIALS
|
||||
int with_creds;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pa_pstream {
|
||||
|
|
@ -76,8 +79,6 @@ struct pa_pstream {
|
|||
pa_queue *send_queue;
|
||||
|
||||
int dead;
|
||||
void (*die_callback) (pa_pstream *p, void *userdata);
|
||||
void *die_callback_userdata;
|
||||
|
||||
struct {
|
||||
struct item_info* current;
|
||||
|
|
@ -94,16 +95,25 @@ struct pa_pstream {
|
|||
size_t index;
|
||||
} read;
|
||||
|
||||
void (*recieve_packet_callback) (pa_pstream *p, pa_packet *packet, void *userdata);
|
||||
pa_pstream_packet_cb_t recieve_packet_callback;
|
||||
void *recieve_packet_callback_userdata;
|
||||
|
||||
void (*recieve_memblock_callback) (pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
|
||||
pa_pstream_memblock_cb_t recieve_memblock_callback;
|
||||
void *recieve_memblock_callback_userdata;
|
||||
|
||||
void (*drain_callback)(pa_pstream *p, void *userdata);
|
||||
void *drain_userdata;
|
||||
pa_pstream_notify_cb_t drain_callback;
|
||||
void *drain_callback_userdata;
|
||||
|
||||
pa_pstream_notify_cb_t die_callback;
|
||||
void *die_callback_userdata;
|
||||
|
||||
pa_memblock_stat *memblock_stat;
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
int send_creds_now;
|
||||
struct ucred ucred;
|
||||
int creds_valid;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int do_write(pa_pstream *p);
|
||||
|
|
@ -170,8 +180,6 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_sta
|
|||
pa_iochannel_set_callback(io, io_callback, p);
|
||||
|
||||
p->dead = 0;
|
||||
p->die_callback = NULL;
|
||||
p->die_callback_userdata = NULL;
|
||||
|
||||
p->mainloop = m;
|
||||
p->defer_event = m->defer_new(m, defer_callback, p);
|
||||
|
|
@ -194,13 +202,20 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_sta
|
|||
p->recieve_memblock_callback_userdata = NULL;
|
||||
|
||||
p->drain_callback = NULL;
|
||||
p->drain_userdata = NULL;
|
||||
p->drain_callback_userdata = NULL;
|
||||
|
||||
p->die_callback = NULL;
|
||||
p->die_callback_userdata = NULL;
|
||||
|
||||
p->memblock_stat = s;
|
||||
|
||||
pa_iochannel_socket_set_rcvbuf(io, 1024*8);
|
||||
pa_iochannel_socket_set_sndbuf(io, 1024*8);
|
||||
pa_iochannel_socket_set_sndbuf(io, 1024*8);
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
p->send_creds_now = 0;
|
||||
p->creds_valid = 0;
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +254,7 @@ static void pstream_free(pa_pstream *p) {
|
|||
pa_xfree(p);
|
||||
}
|
||||
|
||||
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) {
|
||||
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, int with_creds) {
|
||||
struct item_info *i;
|
||||
assert(p && packet && p->ref >= 1);
|
||||
|
||||
|
|
@ -251,6 +266,9 @@ void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) {
|
|||
i = pa_xnew(struct item_info, 1);
|
||||
i->type = PA_PSTREAM_ITEM_PACKET;
|
||||
i->packet = pa_packet_ref(packet);
|
||||
#ifdef SCM_CREDENTIALS
|
||||
i->with_creds = with_creds;
|
||||
#endif
|
||||
|
||||
pa_queue_push(p->send_queue, i);
|
||||
p->mainloop->defer_enable(p->defer_event, 1);
|
||||
|
|
@ -278,20 +296,6 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
|
|||
p->mainloop->defer_enable(p->defer_event, 1);
|
||||
}
|
||||
|
||||
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata) {
|
||||
assert(p && callback);
|
||||
|
||||
p->recieve_packet_callback = callback;
|
||||
p->recieve_packet_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, int64_t delta, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata), void *userdata) {
|
||||
assert(p && callback);
|
||||
|
||||
p->recieve_memblock_callback = callback;
|
||||
p->recieve_memblock_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
static void prepare_next_write_item(pa_pstream *p) {
|
||||
assert(p);
|
||||
|
||||
|
|
@ -310,6 +314,11 @@ static void prepare_next_write_item(pa_pstream *p) {
|
|||
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = 0;
|
||||
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0;
|
||||
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK] = 0;
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
p->send_creds_now = 1;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock);
|
||||
p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index;
|
||||
|
|
@ -318,6 +327,10 @@ static void prepare_next_write_item(pa_pstream *p) {
|
|||
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));
|
||||
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset));
|
||||
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK] = htonl(p->write.current->seek_mode);
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
p->send_creds_now = 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,6 +356,16 @@ static int do_write(pa_pstream *p) {
|
|||
l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
|
||||
}
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
if (p->send_creds_now) {
|
||||
|
||||
if ((r = pa_iochannel_write_with_creds(p->io, d, l)) < 0)
|
||||
return -1;
|
||||
|
||||
p->send_creds_now = 0;
|
||||
} else
|
||||
#endif
|
||||
|
||||
if ((r = pa_iochannel_write(p->io, d, l)) < 0)
|
||||
return -1;
|
||||
|
||||
|
|
@ -354,7 +377,7 @@ static int do_write(pa_pstream *p) {
|
|||
p->write.current = NULL;
|
||||
|
||||
if (p->drain_callback && !pa_pstream_is_pending(p))
|
||||
p->drain_callback(p, p->drain_userdata);
|
||||
p->drain_callback(p, p->drain_callback_userdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -375,8 +398,19 @@ static int do_read(pa_pstream *p) {
|
|||
l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
|
||||
}
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
{
|
||||
int b;
|
||||
|
||||
if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->ucred, &b)) <= 0)
|
||||
return -1;
|
||||
|
||||
p->creds_valid = p->creds_valid || b;
|
||||
}
|
||||
#else
|
||||
if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
p->read.index += r;
|
||||
|
||||
|
|
@ -453,25 +487,59 @@ static int do_read(pa_pstream *p) {
|
|||
assert(p->read.packet);
|
||||
|
||||
if (p->recieve_packet_callback)
|
||||
p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata);
|
||||
#ifdef SCM_CREDENTIALS
|
||||
p->recieve_packet_callback(p, p->read.packet, p->creds_valid ? &p->ucred : NULL, p->recieve_packet_callback_userdata);
|
||||
#else
|
||||
p->recieve_packet_callback(p, p->read.packet, NULL, p->recieve_packet_callback_userdata);
|
||||
#endif
|
||||
|
||||
pa_packet_unref(p->read.packet);
|
||||
p->read.packet = NULL;
|
||||
}
|
||||
|
||||
p->read.index = 0;
|
||||
#ifdef SCM_CREDENTIALS
|
||||
p->creds_valid = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata) {
|
||||
assert(p && callback);
|
||||
p->die_callback = callback;
|
||||
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
|
||||
assert(p);
|
||||
assert(p->ref >= 1);
|
||||
|
||||
p->die_callback = cb;
|
||||
p->die_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
|
||||
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
|
||||
assert(p);
|
||||
assert(p->ref >= 1);
|
||||
|
||||
p->drain_callback = cb;
|
||||
p->drain_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) {
|
||||
assert(p);
|
||||
assert(p->ref >= 1);
|
||||
|
||||
p->recieve_packet_callback = cb;
|
||||
p->recieve_packet_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) {
|
||||
assert(p);
|
||||
assert(p->ref >= 1);
|
||||
|
||||
p->recieve_memblock_callback = cb;
|
||||
p->recieve_memblock_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
int pa_pstream_is_pending(pa_pstream *p) {
|
||||
assert(p);
|
||||
|
||||
|
|
@ -481,14 +549,6 @@ int pa_pstream_is_pending(pa_pstream *p) {
|
|||
return p->write.current || !pa_queue_is_empty(p->send_queue);
|
||||
}
|
||||
|
||||
void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata) {
|
||||
assert(p);
|
||||
assert(p->ref >= 1);
|
||||
|
||||
p->drain_callback = cb;
|
||||
p->drain_userdata = userdata;
|
||||
}
|
||||
|
||||
void pa_pstream_unref(pa_pstream*p) {
|
||||
assert(p);
|
||||
assert(p->ref >= 1);
|
||||
|
|
|
|||
|
|
@ -33,18 +33,22 @@
|
|||
|
||||
typedef struct pa_pstream pa_pstream;
|
||||
|
||||
typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata);
|
||||
typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
|
||||
typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
|
||||
|
||||
pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s);
|
||||
void pa_pstream_unref(pa_pstream*p);
|
||||
pa_pstream* pa_pstream_ref(pa_pstream*p);
|
||||
|
||||
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet);
|
||||
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, int with_creds);
|
||||
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
|
||||
|
||||
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata);
|
||||
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata), void *userdata);
|
||||
void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata);
|
||||
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata);
|
||||
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata);
|
||||
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
|
||||
|
||||
void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata);
|
||||
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
|
||||
|
||||
int pa_pstream_is_pending(pa_pstream *p);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue