pulseaudio/src/modules/module-pipe-source.c
Tanu Kaskinen 916be59151 pipe-sink, pipe-source: fix file permissions
We recently changed the umask of the daemon from 022 to 077, which broke
module-pipe-sink in the system mode, because nobody was allowed to read
from the pipe.

module-pipe-source in the system mode was probably always broken,
because the old umask of 022 should prevent anyone from writing to the
pipe.

This patch uses chmod() after the file creation to set the permissions
to 0666, which is what the fkfifo() call tried to set.

Bug link: https://bugs.freedesktop.org/show_bug.cgi?id=107070
2018-07-10 14:44:46 +03:00

373 lines
9.9 KiB
C

/***
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
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 <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/poll.h>
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("UNIX pipe source");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(false);
PA_MODULE_USAGE(
"source_name=<name for the source> "
"source_properties=<properties for the source> "
"file=<path of the FIFO> "
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
"channel_map=<channel map>");
#define DEFAULT_FILE_NAME "/tmp/music.input"
#define DEFAULT_SOURCE_NAME "fifo_input"
struct userdata {
pa_core *core;
pa_module *module;
pa_source *source;
pa_thread *thread;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
char *filename;
int fd;
pa_memchunk memchunk;
pa_rtpoll_item *rtpoll_item;
};
static const char* const valid_modargs[] = {
"source_name",
"source_properties",
"file",
"format",
"rate",
"channels",
"channel_map",
NULL
};
static int source_process_msg(
pa_msgobject *o,
int code,
void *data,
int64_t offset,
pa_memchunk *chunk) {
struct userdata *u = PA_SOURCE(o)->userdata;
switch (code) {
case PA_SOURCE_MESSAGE_GET_LATENCY: {
size_t n = 0;
#ifdef FIONREAD
int l;
if (ioctl(u->fd, FIONREAD, &l) >= 0 && l > 0)
n = (size_t) l;
#endif
*((int64_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
return 0;
}
}
return pa_source_process_msg(o, code, data, offset, chunk);
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
int read_type = 0;
pa_assert(u);
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
for (;;) {
int ret;
struct pollfd *pollfd;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
/* Try to read some data and pass it on to the source driver */
if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
ssize_t l;
void *p;
if (!u->memchunk.memblock) {
u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd));
u->memchunk.index = u->memchunk.length = 0;
}
pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);
p = pa_memblock_acquire(u->memchunk.memblock);
l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
pa_memblock_release(u->memchunk.memblock);
pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */
if (l < 0) {
if (errno == EINTR)
continue;
else if (errno != EAGAIN) {
pa_log("Failed to read data from FIFO: %s", pa_cstrerror(errno));
goto fail;
}
} else {
u->memchunk.length = (size_t) l;
pa_source_post(u->source, &u->memchunk);
u->memchunk.index += (size_t) l;
if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset(&u->memchunk);
}
pollfd->revents = 0;
}
}
/* Hmm, nothing to do. Let's sleep */
pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0);
if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
goto fail;
if (ret == 0)
goto finish;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (pollfd->revents & ~POLLIN) {
pa_log("FIFO shutdown.");
goto fail;
}
}
fail:
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
pa_log_debug("Thread shutting down");
}
int pa__init(pa_module *m) {
struct userdata *u;
struct stat st;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
struct pollfd *pollfd;
pa_source_new_data data;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
ss = m->core->default_sample_spec;
map = m->core->default_channel_map;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
pa_log("Invalid sample format specification or channel map");
goto fail;
}
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
pa_memchunk_reset(&u->memchunk);
u->rtpoll = pa_rtpoll_new();
if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
pa_log("pa_thread_mq_init() failed.");
goto fail;
}
u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
if (mkfifo(u->filename, 0666) < 0) {
pa_log("mkfifo('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
} else {
/* Our umask is 077, so the pipe won't be created with the requested
* permissions. Let's fix the permissions with chmod(). */
if (chmod(u->filename, 0666) < 0)
pa_log_warn("chomd('%s'): %s", u->filename, pa_cstrerror(errno));
}
if ((u->fd = pa_open_cloexec(u->filename, O_RDWR, 0)) < 0) {
pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
pa_make_fd_nonblock(u->fd);
if (fstat(u->fd, &st) < 0) {
pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
if (!S_ISFIFO(st.st_mode)) {
pa_log("'%s' is not a FIFO.", u->filename);
goto fail;
}
pa_source_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
pa_source_new_data_set_sample_spec(&data, &ss);
pa_source_new_data_set_channel_map(&data, &map);
if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
pa_log("Invalid properties");
pa_source_new_data_done(&data);
goto fail;
}
u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
pa_source_new_data_done(&data);
if (!u->source) {
pa_log("Failed to create source.");
goto fail;
}
u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(pa_pipe_buf(u->fd), &u->source->sample_spec));
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->fd;
pollfd->events = pollfd->revents = 0;
if (!(u->thread = pa_thread_new("pipe-source", thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
}
pa_source_put(u->source);
pa_modargs_free(ma);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
pa__done(m);
return -1;
}
int pa__get_n_used(pa_module *m) {
struct userdata *u;
pa_assert(m);
pa_assert_se(u = m->userdata);
return pa_source_linked_by(u->source);
}
void pa__done(pa_module *m) {
struct userdata *u;
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->source)
pa_source_unlink(u->source);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
pa_thread_mq_done(&u->thread_mq);
if (u->source)
pa_source_unref(u->source);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
if (u->rtpoll_item)
pa_rtpoll_item_free(u->rtpoll_item);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
if (u->filename) {
unlink(u->filename);
pa_xfree(u->filename);
}
if (u->fd >= 0)
pa_assert_se(pa_close(u->fd) == 0);
pa_xfree(u);
}