alsa-lib/aserver/aserver.c
Takashi Iwai dbb7eca655 Remove unused hostname resolution in shm plugins and aserver
PCM and control shm plugins and aserver have some codes to resolve the
host address and check whether it's a local host although the given
address is never used.  In addition, the code contains gethostbyname()
that is known to be obsoleted.  So, let's get rid of all these unused
codes.

The host configuration item is still accepted (but just ignored) for
keeping the compatibility.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-01-28 16:23:06 +01:00

1084 lines
25 KiB
C

/*
* ALSA server
* Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stddef.h>
#include <getopt.h>
#include <netinet/in.h>
#include <netdb.h>
#include <limits.h>
#include <signal.h>
#include "aserver.h"
char *command;
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define ERROR(...) do {\
fprintf(stderr, "%s %s:%i:(%s) ", command, __FILE__, __LINE__, __FUNCTION__); \
fprintf(stderr, __VA_ARGS__); \
putc('\n', stderr); \
} while (0)
#else
#define ERROR(args...) do {\
fprintf(stderr, "%s %s:%i:(%s) ", command, __FILE__, __LINE__, __FUNCTION__); \
fprintf(stderr, ##args); \
putc('\n', stderr); \
} while (0)
#endif
#define SYSERROR(string) ERROR(string ": %s", strerror(errno))
static int make_local_socket(const char *filename)
{
size_t l = strlen(filename);
size_t size = offsetof(struct sockaddr_un, sun_path) + l;
struct sockaddr_un *addr = alloca(size);
int sock;
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0) {
int result = -errno;
SYSERROR("socket failed");
return result;
}
unlink(filename);
addr->sun_family = AF_LOCAL;
memcpy(addr->sun_path, filename, l);
if (bind(sock, (struct sockaddr *) addr, size) < 0) {
int result = -errno;
SYSERROR("bind failed");
return result;
}
return sock;
}
static int make_inet_socket(int port)
{
struct sockaddr_in addr;
int sock;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
int result = -errno;
SYSERROR("socket failed");
return result;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
int result = -errno;
SYSERROR("bind failed");
return result;
}
return sock;
}
struct pollfd *pollfds;
unsigned int pollfds_count = 0;
typedef struct waiter waiter_t;
typedef int (*waiter_handler_t)(waiter_t *waiter, unsigned short events);
struct waiter {
int fd;
void *private_data;
waiter_handler_t handler;
};
waiter_t *waiters;
static void add_waiter(int fd, unsigned short events, waiter_handler_t handler,
void *data)
{
waiter_t *w = &waiters[fd];
struct pollfd *pfd = &pollfds[pollfds_count];
assert(!w->handler);
pfd->fd = fd;
pfd->events = events;
pfd->revents = 0;
w->fd = fd;
w->private_data = data;
w->handler = handler;
pollfds_count++;
}
static void del_waiter(int fd)
{
waiter_t *w = &waiters[fd];
unsigned int k;
assert(w->handler);
w->handler = 0;
for (k = 0; k < pollfds_count; ++k) {
if (pollfds[k].fd == fd)
break;
}
assert(k < pollfds_count);
pollfds_count--;
memmove(&pollfds[k], &pollfds[k + 1], pollfds_count - k);
}
typedef struct client client_t;
typedef struct {
int (*open)(client_t *client, int *cookie);
int (*cmd)(client_t *client);
int (*close)(client_t *client);
} transport_ops_t;
struct client {
struct list_head list;
int poll_fd;
int ctrl_fd;
int local;
int transport_type;
int dev_type;
char name[256];
int stream;
int mode;
transport_ops_t *ops;
snd_async_handler_t *async_handler;
int async_sig;
pid_t async_pid;
union {
struct {
snd_pcm_t *handle;
int fd;
} pcm;
struct {
snd_ctl_t *handle;
int fd;
} ctl;
#if 0
struct {
snd_rawmidi_t *handle;
} rawmidi;
struct {
snd_timer_open_t *handle;
} timer;
struct {
snd_hwdep_t *handle;
} hwdep;
struct {
snd_seq_t *handle;
} seq;
#endif
} device;
int polling;
int open;
int cookie;
union {
struct {
int ctrl_id;
void *ctrl;
} shm;
} transport;
};
LIST_HEAD(clients);
typedef struct {
struct list_head list;
int fd;
uint32_t cookie;
} inet_pending_t;
LIST_HEAD(inet_pendings);
#if 0
static int pcm_handler(waiter_t *waiter, unsigned short events)
{
client_t *client = waiter->private_data;
char buf[1];
ssize_t n;
if (events & POLLIN) {
n = write(client->poll_fd, buf, 1);
if (n != 1) {
SYSERROR("write failed");
return -errno;
}
} else if (events & POLLOUT) {
n = read(client->poll_fd, buf, 1);
if (n != 1) {
SYSERROR("read failed");
return -errno;
}
}
del_waiter(waiter->fd);
client->polling = 0;
return 0;
}
#endif
static void pcm_shm_hw_ptr_changed(snd_pcm_t *pcm, snd_pcm_t *src ATTRIBUTE_UNUSED)
{
client_t *client = pcm->hw.private_data;
volatile snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
snd_pcm_t *loop;
ctrl->hw.changed = 1;
if (pcm->hw.fd >= 0) {
ctrl->hw.use_mmap = 1;
ctrl->hw.offset = pcm->hw.offset;
return;
}
ctrl->hw.use_mmap = 0;
ctrl->hw.ptr = pcm->hw.ptr ? *pcm->hw.ptr : 0;
for (loop = pcm->hw.master; loop; loop = loop->hw.master)
loop->hw.ptr = &ctrl->hw.ptr;
pcm->hw.ptr = &ctrl->hw.ptr;
}
static void pcm_shm_appl_ptr_changed(snd_pcm_t *pcm, snd_pcm_t *src ATTRIBUTE_UNUSED)
{
client_t *client = pcm->appl.private_data;
volatile snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
snd_pcm_t *loop;
ctrl->appl.changed = 1;
if (pcm->appl.fd >= 0) {
ctrl->appl.use_mmap = 1;
ctrl->appl.offset = pcm->appl.offset;
return;
}
ctrl->appl.use_mmap = 0;
ctrl->appl.ptr = pcm->appl.ptr ? *pcm->appl.ptr : 0;
for (loop = pcm->appl.master; loop; loop = loop->appl.master)
loop->appl.ptr = &ctrl->appl.ptr;
pcm->appl.ptr = &ctrl->appl.ptr;
}
static int pcm_shm_open(client_t *client, int *cookie)
{
int shmid;
snd_pcm_t *pcm;
int err;
int result;
err = snd_pcm_open(&pcm, client->name, client->stream, SND_PCM_NONBLOCK);
if (err < 0)
return err;
client->device.pcm.handle = pcm;
client->device.pcm.fd = _snd_pcm_poll_descriptor(pcm);
pcm->hw.private_data = client;
pcm->hw.changed = pcm_shm_hw_ptr_changed;
pcm->appl.private_data = client;
pcm->appl.changed = pcm_shm_appl_ptr_changed;
shmid = shmget(IPC_PRIVATE, PCM_SHM_SIZE, 0666);
if (shmid < 0) {
result = -errno;
SYSERROR("shmget failed");
goto _err;
}
client->transport.shm.ctrl_id = shmid;
client->transport.shm.ctrl = shmat(shmid, 0, 0);
if (client->transport.shm.ctrl == (void*) -1) {
result = -errno;
shmctl(shmid, IPC_RMID, 0);
SYSERROR("shmat failed");
goto _err;
}
*cookie = shmid;
return 0;
_err:
snd_pcm_close(pcm);
return result;
}
static int pcm_shm_close(client_t *client)
{
int err;
snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
if (client->polling) {
del_waiter(client->device.pcm.fd);
client->polling = 0;
}
err = snd_pcm_close(client->device.pcm.handle);
ctrl->result = err;
if (err < 0)
ERROR("snd_pcm_close");
if (client->transport.shm.ctrl) {
err = shmdt((void *)client->transport.shm.ctrl);
if (err < 0)
SYSERROR("shmdt failed");
err = shmctl(client->transport.shm.ctrl_id, IPC_RMID, 0);
if (err < 0)
SYSERROR("shmctl IPC_RMID failed");
client->transport.shm.ctrl = 0;
}
client->open = 0;
return 0;
}
static int shm_ack(client_t *client)
{
struct pollfd pfd;
int err;
char buf[1];
pfd.fd = client->ctrl_fd;
pfd.events = POLLHUP;
if (poll(&pfd, 1, 0) == 1)
return -EBADFD;
err = write(client->ctrl_fd, buf, 1);
if (err != 1)
return -EBADFD;
return 0;
}
static int shm_ack_fd(client_t *client, int fd)
{
struct pollfd pfd;
int err;
char buf[1];
pfd.fd = client->ctrl_fd;
pfd.events = POLLHUP;
if (poll(&pfd, 1, 0) == 1)
return -EBADFD;
err = snd_send_fd(client->ctrl_fd, buf, 1, fd);
if (err != 1)
return -EBADFD;
return 0;
}
static int shm_rbptr_fd(client_t *client, snd_pcm_rbptr_t *rbptr)
{
if (rbptr->fd < 0)
return -EINVAL;
return shm_ack_fd(client, rbptr->fd);
}
static void async_handler(snd_async_handler_t *handler)
{
client_t *client = snd_async_handler_get_callback_private(handler);
/* FIXME: use sigqueue */
kill(client->async_pid, client->async_sig);
}
static int pcm_shm_cmd(client_t *client)
{
volatile snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
char buf[1];
int err;
int cmd;
snd_pcm_t *pcm;
err = read(client->ctrl_fd, buf, 1);
if (err != 1)
return -EBADFD;
cmd = ctrl->cmd;
ctrl->cmd = 0;
pcm = client->device.pcm.handle;
switch (cmd) {
case SND_PCM_IOCTL_ASYNC:
ctrl->result = snd_pcm_async(pcm, ctrl->u.async.sig, ctrl->u.async.pid);
if (ctrl->result < 0)
break;
if (ctrl->u.async.sig >= 0) {
assert(client->async_sig < 0);
ctrl->result = snd_async_add_pcm_handler(&client->async_handler, pcm, async_handler, client);
if (ctrl->result < 0)
break;
} else {
assert(client->async_sig >= 0);
snd_async_del_handler(client->async_handler);
}
client->async_sig = ctrl->u.async.sig;
client->async_pid = ctrl->u.async.pid;
break;
case SNDRV_PCM_IOCTL_INFO:
ctrl->result = snd_pcm_info(pcm, (snd_pcm_info_t *) &ctrl->u.info);
break;
case SNDRV_PCM_IOCTL_HW_REFINE:
ctrl->result = snd_pcm_hw_refine(pcm, (snd_pcm_hw_params_t *) &ctrl->u.hw_refine);
break;
case SNDRV_PCM_IOCTL_HW_PARAMS:
ctrl->result = snd_pcm_hw_params(pcm, (snd_pcm_hw_params_t *) &ctrl->u.hw_params);
break;
case SNDRV_PCM_IOCTL_HW_FREE:
ctrl->result = snd_pcm_hw_free(pcm);
break;
case SNDRV_PCM_IOCTL_SW_PARAMS:
ctrl->result = snd_pcm_sw_params(pcm, (snd_pcm_sw_params_t *) &ctrl->u.sw_params);
break;
case SNDRV_PCM_IOCTL_STATUS:
ctrl->result = snd_pcm_status(pcm, (snd_pcm_status_t *) &ctrl->u.status);
break;
case SND_PCM_IOCTL_STATE:
ctrl->result = snd_pcm_state(pcm);
break;
case SND_PCM_IOCTL_HWSYNC:
ctrl->result = snd_pcm_hwsync(pcm);
break;
case SNDRV_PCM_IOCTL_DELAY:
ctrl->result = snd_pcm_delay(pcm, (snd_pcm_sframes_t *) &ctrl->u.delay.frames);
break;
case SND_PCM_IOCTL_AVAIL_UPDATE:
ctrl->result = snd_pcm_avail_update(pcm);
break;
case SNDRV_PCM_IOCTL_PREPARE:
ctrl->result = snd_pcm_prepare(pcm);
break;
case SNDRV_PCM_IOCTL_RESET:
ctrl->result = snd_pcm_reset(pcm);
break;
case SNDRV_PCM_IOCTL_START:
ctrl->result = snd_pcm_start(pcm);
break;
case SNDRV_PCM_IOCTL_DRAIN:
ctrl->result = snd_pcm_drain(pcm);
break;
case SNDRV_PCM_IOCTL_DROP:
ctrl->result = snd_pcm_drop(pcm);
break;
case SNDRV_PCM_IOCTL_PAUSE:
ctrl->result = snd_pcm_pause(pcm, ctrl->u.pause.enable);
break;
case SNDRV_PCM_IOCTL_CHANNEL_INFO:
ctrl->result = snd_pcm_channel_info(pcm, (snd_pcm_channel_info_t *) &ctrl->u.channel_info);
if (ctrl->result >= 0 &&
ctrl->u.channel_info.type == SND_PCM_AREA_MMAP)
return shm_ack_fd(client, ctrl->u.channel_info.u.mmap.fd);
break;
case SNDRV_PCM_IOCTL_REWIND:
ctrl->result = snd_pcm_rewind(pcm, ctrl->u.rewind.frames);
break;
case SND_PCM_IOCTL_FORWARD:
ctrl->result = snd_pcm_forward(pcm, ctrl->u.forward.frames);
break;
case SNDRV_PCM_IOCTL_LINK:
{
/* FIXME */
ctrl->result = -ENOSYS;
break;
}
case SNDRV_PCM_IOCTL_UNLINK:
ctrl->result = snd_pcm_unlink(pcm);
break;
case SNDRV_PCM_IOCTL_RESUME:
ctrl->result = snd_pcm_resume(pcm);
break;
case SND_PCM_IOCTL_MMAP:
{
ctrl->result = snd_pcm_mmap(pcm);
}
case SND_PCM_IOCTL_MUNMAP:
{
ctrl->result = snd_pcm_munmap(pcm);
break;
}
case SND_PCM_IOCTL_MMAP_COMMIT:
ctrl->result = snd_pcm_mmap_commit(pcm,
ctrl->u.mmap_commit.offset,
ctrl->u.mmap_commit.frames);
break;
case SND_PCM_IOCTL_POLL_DESCRIPTOR:
ctrl->result = 0;
return shm_ack_fd(client, _snd_pcm_poll_descriptor(pcm));
case SND_PCM_IOCTL_CLOSE:
client->ops->close(client);
break;
case SND_PCM_IOCTL_HW_PTR_FD:
return shm_rbptr_fd(client, &pcm->hw);
case SND_PCM_IOCTL_APPL_PTR_FD:
return shm_rbptr_fd(client, &pcm->appl);
default:
ERROR("Bogus cmd: %x", ctrl->cmd);
ctrl->result = -ENOSYS;
}
return shm_ack(client);
}
transport_ops_t pcm_shm_ops = {
.open = pcm_shm_open,
.cmd = pcm_shm_cmd,
.close = pcm_shm_close,
};
static int ctl_handler(waiter_t *waiter, unsigned short events)
{
client_t *client = waiter->private_data;
char buf[1];
ssize_t n;
if (events & POLLIN) {
n = write(client->poll_fd, buf, 1);
if (n != 1) {
SYSERROR("write failed");
return -errno;
}
}
del_waiter(waiter->fd);
client->polling = 0;
return 0;
}
static int ctl_shm_open(client_t *client, int *cookie)
{
int shmid;
snd_ctl_t *ctl;
int err;
int result;
err = snd_ctl_open(&ctl, client->name, SND_CTL_NONBLOCK);
if (err < 0)
return err;
client->device.ctl.handle = ctl;
client->device.ctl.fd = _snd_ctl_poll_descriptor(ctl);
shmid = shmget(IPC_PRIVATE, CTL_SHM_SIZE, 0666);
if (shmid < 0) {
result = -errno;
SYSERROR("shmget failed");
goto _err;
}
client->transport.shm.ctrl_id = shmid;
client->transport.shm.ctrl = shmat(shmid, 0, 0);
if (!client->transport.shm.ctrl) {
result = -errno;
shmctl(shmid, IPC_RMID, 0);
SYSERROR("shmat failed");
goto _err;
}
*cookie = shmid;
add_waiter(client->device.ctl.fd, POLLIN, ctl_handler, client);
client->polling = 1;
return 0;
_err:
snd_ctl_close(ctl);
return result;
}
static int ctl_shm_close(client_t *client)
{
int err;
snd_ctl_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
if (client->polling) {
del_waiter(client->device.ctl.fd);
client->polling = 0;
}
err = snd_ctl_close(client->device.ctl.handle);
ctrl->result = err;
if (err < 0)
ERROR("snd_ctl_close");
if (client->transport.shm.ctrl) {
err = shmdt((void *)client->transport.shm.ctrl);
if (err < 0)
SYSERROR("shmdt failed");
err = shmctl(client->transport.shm.ctrl_id, IPC_RMID, 0);
if (err < 0)
SYSERROR("shmctl failed");
client->transport.shm.ctrl = 0;
}
client->open = 0;
return 0;
}
static int ctl_shm_cmd(client_t *client)
{
snd_ctl_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
char buf[1];
int err;
int cmd;
snd_ctl_t *ctl;
err = read(client->ctrl_fd, buf, 1);
if (err != 1)
return -EBADFD;
cmd = ctrl->cmd;
ctrl->cmd = 0;
ctl = client->device.ctl.handle;
switch (cmd) {
case SND_CTL_IOCTL_ASYNC:
ctrl->result = snd_ctl_async(ctl, ctrl->u.async.sig, ctrl->u.async.pid);
if (ctrl->result < 0)
break;
if (ctrl->u.async.sig >= 0) {
assert(client->async_sig < 0);
ctrl->result = snd_async_add_ctl_handler(&client->async_handler, ctl, async_handler, client);
if (ctrl->result < 0)
break;
} else {
assert(client->async_sig >= 0);
snd_async_del_handler(client->async_handler);
}
client->async_sig = ctrl->u.async.sig;
client->async_pid = ctrl->u.async.pid;
break;
break;
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
ctrl->result = snd_ctl_subscribe_events(ctl, ctrl->u.subscribe_events);
break;
case SNDRV_CTL_IOCTL_CARD_INFO:
ctrl->result = snd_ctl_card_info(ctl, &ctrl->u.card_info);
break;
case SNDRV_CTL_IOCTL_ELEM_LIST:
{
size_t maxsize = CTL_SHM_DATA_MAXLEN;
if (ctrl->u.element_list.space * sizeof(*ctrl->u.element_list.pids) > maxsize) {
ctrl->result = -EFAULT;
break;
}
ctrl->u.element_list.pids = (snd_ctl_elem_id_t*) ctrl->data;
ctrl->result = snd_ctl_elem_list(ctl, &ctrl->u.element_list);
break;
}
case SNDRV_CTL_IOCTL_ELEM_INFO:
ctrl->result = snd_ctl_elem_info(ctl, &ctrl->u.element_info);
break;
case SNDRV_CTL_IOCTL_ELEM_READ:
ctrl->result = snd_ctl_elem_read(ctl, &ctrl->u.element_read);
break;
case SNDRV_CTL_IOCTL_ELEM_WRITE:
ctrl->result = snd_ctl_elem_write(ctl, &ctrl->u.element_write);
break;
case SNDRV_CTL_IOCTL_ELEM_LOCK:
ctrl->result = snd_ctl_elem_lock(ctl, &ctrl->u.element_lock);
break;
case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
ctrl->result = snd_ctl_elem_unlock(ctl, &ctrl->u.element_unlock);
break;
case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
ctrl->result = snd_ctl_hwdep_next_device(ctl, &ctrl->u.device);
break;
case SNDRV_CTL_IOCTL_HWDEP_INFO:
ctrl->result = snd_ctl_hwdep_info(ctl, &ctrl->u.hwdep_info);
break;
case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
ctrl->result = snd_ctl_pcm_next_device(ctl, &ctrl->u.device);
break;
case SNDRV_CTL_IOCTL_PCM_INFO:
ctrl->result = snd_ctl_pcm_info(ctl, &ctrl->u.pcm_info);
break;
case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
ctrl->result = snd_ctl_pcm_prefer_subdevice(ctl, ctrl->u.pcm_prefer_subdevice);
break;
case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
ctrl->result = snd_ctl_rawmidi_next_device(ctl, &ctrl->u.device);
break;
case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
ctrl->result = snd_ctl_rawmidi_info(ctl, &ctrl->u.rawmidi_info);
break;
case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
ctrl->result = snd_ctl_rawmidi_prefer_subdevice(ctl, ctrl->u.rawmidi_prefer_subdevice);
break;
case SNDRV_CTL_IOCTL_POWER:
ctrl->result = snd_ctl_set_power_state(ctl, ctrl->u.power_state);
break;
case SNDRV_CTL_IOCTL_POWER_STATE:
ctrl->result = snd_ctl_get_power_state(ctl, &ctrl->u.power_state);
break;
case SND_CTL_IOCTL_READ:
ctrl->result = snd_ctl_read(ctl, &ctrl->u.read);
break;
case SND_CTL_IOCTL_CLOSE:
client->ops->close(client);
break;
case SND_CTL_IOCTL_POLL_DESCRIPTOR:
ctrl->result = 0;
return shm_ack_fd(client, _snd_ctl_poll_descriptor(ctl));
default:
ERROR("Bogus cmd: %x", ctrl->cmd);
ctrl->result = -ENOSYS;
}
return shm_ack(client);
}
transport_ops_t ctl_shm_ops = {
.open = ctl_shm_open,
.cmd = ctl_shm_cmd,
.close = ctl_shm_close,
};
static int snd_client_open(client_t *client)
{
int err;
snd_client_open_request_t req;
snd_client_open_answer_t ans;
char *name;
memset(&ans, 0, sizeof(ans));
err = read(client->ctrl_fd, &req, sizeof(req));
if (err < 0) {
SYSERROR("read failed");
exit(1);
}
if (err != sizeof(req)) {
ans.result = -EINVAL;
goto _answer;
}
name = alloca(req.namelen);
err = read(client->ctrl_fd, name, req.namelen);
if (err < 0) {
SYSERROR("read failed");
exit(1);
}
if (err != req.namelen) {
ans.result = -EINVAL;
goto _answer;
}
switch (req.transport_type) {
case SND_TRANSPORT_TYPE_SHM:
if (!client->local) {
ans.result = -EINVAL;
goto _answer;
}
switch (req.dev_type) {
case SND_DEV_TYPE_PCM:
client->ops = &pcm_shm_ops;
break;
case SND_DEV_TYPE_CONTROL:
client->ops = &ctl_shm_ops;
break;
default:
ans.result = -EINVAL;
goto _answer;
}
break;
default:
ans.result = -EINVAL;
goto _answer;
}
name[req.namelen] = '\0';
client->transport_type = req.transport_type;
strcpy(client->name, name);
client->stream = req.stream;
client->mode = req.mode;
err = client->ops->open(client, &ans.cookie);
if (err < 0) {
ans.result = err;
} else {
client->open = 1;
ans.result = 0;
}
_answer:
err = write(client->ctrl_fd, &ans, sizeof(ans));
if (err != sizeof(ans)) {
SYSERROR("write failed");
exit(1);
}
return 0;
}
static int client_poll_handler(waiter_t *waiter, unsigned short events ATTRIBUTE_UNUSED)
{
client_t *client = waiter->private_data;
if (client->open)
client->ops->close(client);
close(client->poll_fd);
close(client->ctrl_fd);
del_waiter(client->poll_fd);
del_waiter(client->ctrl_fd);
list_del(&client->list);
free(client);
return 0;
}
static int client_ctrl_handler(waiter_t *waiter, unsigned short events)
{
client_t *client = waiter->private_data;
if (events & POLLHUP) {
if (client->open)
client->ops->close(client);
close(client->ctrl_fd);
del_waiter(client->ctrl_fd);
list_del(&client->list);
free(client);
return 0;
}
if (client->open)
return client->ops->cmd(client);
else
return snd_client_open(client);
}
static int inet_pending_handler(waiter_t *waiter, unsigned short events)
{
inet_pending_t *pending = waiter->private_data;
inet_pending_t *pdata;
client_t *client;
uint32_t cookie;
struct list_head *item;
int remove = 0;
if (events & POLLHUP)
remove = 1;
else {
int err = read(waiter->fd, &cookie, sizeof(cookie));
if (err != sizeof(cookie))
remove = 1;
else {
err = write(waiter->fd, &cookie, sizeof(cookie));
if (err != sizeof(cookie))
remove = 1;
}
}
del_waiter(waiter->fd);
if (remove) {
close(waiter->fd);
list_del(&pending->list);
free(pending);
return 0;
}
list_for_each(item, &inet_pendings) {
pdata = list_entry(item, inet_pending_t, list);
if (pdata->cookie == cookie)
goto found;
}
pending->cookie = cookie;
return 0;
found:
client = calloc(1, sizeof(*client));
client->local = 0;
client->poll_fd = pdata->fd;
client->ctrl_fd = waiter->fd;
add_waiter(client->ctrl_fd, POLLIN | POLLHUP, client_ctrl_handler, client);
add_waiter(client->poll_fd, POLLHUP, client_poll_handler, client);
client->open = 0;
list_add_tail(&client->list, &clients);
list_del(&pending->list);
list_del(&pdata->list);
free(pending);
free(pdata);
return 0;
}
static int local_handler(waiter_t *waiter, unsigned short events ATTRIBUTE_UNUSED)
{
int sock;
sock = accept(waiter->fd, 0, 0);
if (sock < 0) {
int result = -errno;
SYSERROR("accept failed");
return result;
} else {
client_t *client = calloc(1, sizeof(*client));
client->ctrl_fd = sock;
client->local = 1;
client->open = 0;
add_waiter(sock, POLLIN | POLLHUP, client_ctrl_handler, client);
list_add_tail(&client->list, &clients);
}
return 0;
}
static int inet_handler(waiter_t *waiter, unsigned short events ATTRIBUTE_UNUSED)
{
int sock;
sock = accept(waiter->fd, 0, 0);
if (sock < 0) {
int result = -errno;
SYSERROR("accept failed");
return result;
} else {
inet_pending_t *pending = calloc(1, sizeof(*pending));
pending->fd = sock;
pending->cookie = 0;
add_waiter(sock, POLLIN, inet_pending_handler, pending);
list_add_tail(&pending->list, &inet_pendings);
}
return 0;
}
static int server(const char *sockname, int port)
{
int err;
unsigned int k;
long open_max;
int result;
if (!sockname && port < 0)
return -EINVAL;
open_max = sysconf(_SC_OPEN_MAX);
if (open_max < 0) {
result = -errno;
SYSERROR("sysconf failed");
return result;
}
pollfds = calloc((size_t) open_max, sizeof(*pollfds));
waiters = calloc((size_t) open_max, sizeof(*waiters));
if (sockname) {
int sock = make_local_socket(sockname);
if (sock < 0)
return sock;
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
result = -errno;
SYSERROR("fcntl O_NONBLOCK failed");
goto _end;
}
if (listen(sock, 4) < 0) {
result = -errno;
SYSERROR("listen failed");
goto _end;
}
add_waiter(sock, POLLIN, local_handler, NULL);
}
if (port >= 0) {
int sock = make_inet_socket(port);
if (sock < 0)
return sock;
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
result = -errno;
SYSERROR("fcntl failed");
goto _end;
}
if (listen(sock, 4) < 0) {
result = -errno;
SYSERROR("listen failed");
goto _end;
}
add_waiter(sock, POLLIN, inet_handler, NULL);
}
while (1) {
struct pollfd pfds[open_max];
size_t pfds_count;
do {
err = poll(pollfds, pollfds_count, -1);
} while (err == 0);
if (err < 0) {
SYSERROR("poll failed");
continue;
}
pfds_count = pollfds_count;
memcpy(pfds, pollfds, sizeof(*pfds) * pfds_count);
for (k = 0; k < pfds_count; k++) {
struct pollfd *pfd = &pfds[k];
if (pfd->revents) {
waiter_t *w = &waiters[pfd->fd];
if (!w->handler)
continue;
err = w->handler(w, pfd->revents);
if (err < 0)
ERROR("waiter handler failed");
}
}
}
_end:
free(pollfds);
free(waiters);
return result;
}
static void usage(void)
{
fprintf(stderr,
"Usage: %s [OPTIONS] server\n"
"--help help\n",
command);
}
int main(int argc, char **argv)
{
static const struct option long_options[] = {
{"help", 0, 0, 'h'},
{ 0 , 0 , 0, 0 }
};
int c;
snd_config_t *conf;
snd_config_iterator_t i, next;
const char *sockname = NULL;
long port = -1;
int err;
char *srvname;
command = argv[0];
while ((c = getopt_long(argc, argv, "h", long_options, 0)) != -1) {
switch (c) {
case 'h':
usage();
return 0;
default:
fprintf(stderr, "Try `%s --help' for more information\n", command);
return 1;
}
}
if (argc - optind != 1) {
ERROR("you need to specify server name");
return 1;
}
err = snd_config_update();
if (err < 0) {
ERROR("cannot read configuration file");
return 1;
}
srvname = argv[optind];
err = snd_config_search_definition(snd_config, "server", srvname, &conf);
if (err < 0) {
ERROR("Missing definition for server %s", srvname);
return 1;
}
if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for server %s definition", srvname);
return -EINVAL;
}
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "host") == 0)
continue;
if (strcmp(id, "socket") == 0) {
err = snd_config_get_string(n, &sockname);
if (err < 0) {
ERROR("Invalid type for %s", id);
return 1;
}
continue;
}
if (strcmp(id, "port") == 0) {
err = snd_config_get_integer(n, &port);
if (err < 0) {
ERROR("Invalid type for %s", id);
return 1;
}
continue;
}
ERROR("Unknown field %s", id);
return 1;
}
if (!sockname && port < 0) {
ERROR("either socket or port need to be defined");
return 1;
}
server(sockname, port);
return 0;
}