| /*** | |
| This file is part of PulseAudio. | |
| Copyright 2009 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 <string.h> | |
| #include <unistd.h> | |
| #include <stdlib.h> | |
| #include <signal.h> | |
| #include <pulse/xmalloc.h> | |
| #include <pulsecore/module.h> | |
| #include <pulsecore/core.h> | |
| #include <pulsecore/core-util.h> | |
| #include <pulsecore/log.h> | |
| #include <pulse/mainloop-api.h> | |
| #include <pulsecore/core-error.h> | |
| #include <pulsecore/start-child.h> | |
| #include "stdin-util.h" | |
| int fill_buf(struct userdata *u) { | |
| ssize_t r; | |
| pa_assert(u); | |
| if (u->buf_fill >= BUF_MAX) { | |
| pa_log("read buffer overflow"); | |
| return -1; | |
| } | |
| if ((r = pa_read(u->fd, u->buf + u->buf_fill, BUF_MAX - u->buf_fill, &u->fd_type)) <= 0) | |
| return -1; | |
| u->buf_fill += (size_t) r; | |
| return 0; | |
| } | |
| int read_byte(struct userdata *u) { | |
| int ret; | |
| pa_assert(u); | |
| if (u->buf_fill < 1) | |
| if (fill_buf(u) < 0) | |
| return -1; | |
| ret = u->buf[0]; | |
| pa_assert(u->buf_fill > 0); | |
| u->buf_fill--; | |
| memmove(u->buf, u->buf+1, u->buf_fill); | |
| return ret; | |
| } | |
| char *read_string(struct userdata *u) { | |
| pa_assert(u); | |
| for (;;) { | |
| char *e; | |
| if ((e = memchr(u->buf, 0, u->buf_fill))) { | |
| char *ret = pa_xstrdup(u->buf); | |
| u->buf_fill -= (size_t) (e - u->buf +1); | |
| memmove(u->buf, e+1, u->buf_fill); | |
| return ret; | |
| } | |
| if (fill_buf(u) < 0) | |
| return NULL; | |
| } | |
| } | |
| void unload_one_module(struct pa_module_info *m, unsigned i) { | |
| struct userdata *u; | |
| pa_assert(m); | |
| pa_assert(i < m->n_items); | |
| u = m->userdata; | |
| if (m->items[i].index == PA_INVALID_INDEX) | |
| return; | |
| pa_log_debug("Unloading module #%i", m->items[i].index); | |
| pa_module_unload_by_index(u->core, m->items[i].index, true); | |
| m->items[i].index = PA_INVALID_INDEX; | |
| pa_xfree(m->items[i].name); | |
| pa_xfree(m->items[i].args); | |
| m->items[i].name = m->items[i].args = NULL; | |
| } | |
| void unload_all_modules(struct pa_module_info *m) { | |
| unsigned i; | |
| pa_assert(m); | |
| for (i = 0; i < m->n_items; i++) | |
| unload_one_module(m, i); | |
| m->n_items = 0; | |
| } | |
| void load_module( | |
| struct pa_module_info *m, | |
| unsigned i, | |
| const char *name, | |
| const char *args, | |
| bool is_new) { | |
| struct userdata *u; | |
| pa_module *mod; | |
| pa_assert(m); | |
| pa_assert(name); | |
| pa_assert(args); | |
| u = m->userdata; | |
| if (!is_new) { | |
| if (m->items[i].index != PA_INVALID_INDEX && | |
| pa_streq(m->items[i].name, name) && | |
| pa_streq(m->items[i].args, args)) | |
| return; | |
| unload_one_module(m, i); | |
| } | |
| pa_log_debug("Loading module '%s' with args '%s' due to GConf/GSettings configuration.", name, args); | |
| m->items[i].name = pa_xstrdup(name); | |
| m->items[i].args = pa_xstrdup(args); | |
| m->items[i].index = PA_INVALID_INDEX; | |
| if (pa_module_load(&mod, u->core, name, args) < 0) { | |
| pa_log("pa_module_load() failed"); | |
| return; | |
| } | |
| m->items[i].index = mod->index; | |
| } | |
| void module_info_free(void *p) { | |
| struct pa_module_info *m = p; | |
| pa_assert(m); | |
| unload_all_modules(m); | |
| pa_xfree(m->name); | |
| pa_xfree(m); | |
| } | |
| int handle_event(struct userdata *u) { | |
| int opcode; | |
| int ret = 0; | |
| do { | |
| if ((opcode = read_byte(u)) < 0) { | |
| if (errno == EINTR || errno == EAGAIN) | |
| break; | |
| goto fail; | |
| } | |
| switch (opcode) { | |
| case '!': | |
| /* The helper tool is now initialized */ | |
| ret = 1; | |
| break; | |
| case '+': { | |
| char *name; | |
| struct pa_module_info *m; | |
| unsigned i, j; | |
| if (!(name = read_string(u))) | |
| goto fail; | |
| if (!(m = pa_hashmap_get(u->module_infos, name))) { | |
| m = pa_xnew(struct pa_module_info, 1); | |
| m->userdata = u; | |
| m->name = name; | |
| m->n_items = 0; | |
| pa_hashmap_put(u->module_infos, m->name, m); | |
| } else | |
| pa_xfree(name); | |
| i = 0; | |
| while (i < MAX_MODULES) { | |
| char *module, *args; | |
| if (!(module = read_string(u))) { | |
| if (i > m->n_items) m->n_items = i; | |
| goto fail; | |
| } | |
| if (!*module) { | |
| pa_xfree(module); | |
| break; | |
| } | |
| if (!(args = read_string(u))) { | |
| pa_xfree(module); | |
| if (i > m->n_items) m->n_items = i; | |
| goto fail; | |
| } | |
| load_module(m, i, module, args, i >= m->n_items); | |
| i++; | |
| pa_xfree(module); | |
| pa_xfree(args); | |
| } | |
| /* Unload all removed modules */ | |
| for (j = i; j < m->n_items; j++) | |
| unload_one_module(m, j); | |
| m->n_items = i; | |
| break; | |
| } | |
| case '-': { | |
| char *name; | |
| if (!(name = read_string(u))) | |
| goto fail; | |
| pa_hashmap_remove_and_free(u->module_infos, name); | |
| pa_xfree(name); | |
| break; | |
| } | |
| } | |
| } while (u->buf_fill > 0 && ret == 0); | |
| return ret; | |
| fail: | |
| pa_log("Unable to read or parse data from client."); | |
| return -1; | |
| } | |
| void io_event_cb( | |
| pa_mainloop_api*a, | |
| pa_io_event* e, | |
| int fd, | |
| pa_io_event_flags_t events, | |
| void *userdata) { | |
| struct userdata *u = userdata; | |
| if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { | |
| pa_log("Lost I/O connection in module \"%s\"", u->module->name); | |
| goto fail; | |
| } | |
| if (handle_event(u) >= 0) | |
| return; | |
| fail: | |
| if (u->io_event) { | |
| u->core->mainloop->io_free(u->io_event); | |
| u->io_event = NULL; | |
| } | |
| pa_module_unload_request(u->module, true); | |
| } |