Blame aserver/aserver.c

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