mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2025-12-20 07:40:03 +01:00
Simple library that uses sockets for inter-process communication. It provides an API to create server and client objects. Users can add custom handlers in the server, and clients can send requests for those custom handlers.
213 lines
5.1 KiB
C
213 lines
5.1 KiB
C
/* WirePlumber
|
|
*
|
|
* Copyright © 2021 Collabora Ltd.
|
|
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/epoll.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
#include "private.h"
|
|
#include "receiver.h"
|
|
|
|
#include "wpipc.h"
|
|
|
|
#define MAX_SENDERS 128
|
|
|
|
struct wpipc_receiver {
|
|
struct sockaddr_un addr;
|
|
int socket_fd;
|
|
|
|
uint8_t *buffer_read;
|
|
size_t buffer_size;
|
|
|
|
struct epoll_thread epoll_thread;
|
|
bool thread_running;
|
|
|
|
const struct wpipc_receiver_events *events;
|
|
void *events_data;
|
|
|
|
/* for subclasses */
|
|
void *user_data;
|
|
};
|
|
|
|
static bool
|
|
reply_message (struct wpipc_receiver *self,
|
|
int sender_fd,
|
|
uint8_t *buffer,
|
|
size_t size)
|
|
{
|
|
return self->events && self->events->handle_message ?
|
|
self->events->handle_message (self, sender_fd, buffer, size, self->events_data) :
|
|
wpipc_socket_write (sender_fd, buffer, size) == (ssize_t)size;
|
|
}
|
|
|
|
static void
|
|
socket_event_received (struct epoll_thread *t, int fd, void *data)
|
|
{
|
|
/* sender wants to connect, accept connection */
|
|
struct wpipc_receiver *self = data;
|
|
socklen_t addr_size = sizeof(self->addr);
|
|
int sender_fd = accept4 (fd, (struct sockaddr*)&self->addr, &addr_size,
|
|
SOCK_CLOEXEC | SOCK_NONBLOCK);
|
|
struct epoll_event event;
|
|
event.events = EPOLLIN;
|
|
event.data.fd = sender_fd;
|
|
epoll_ctl (t->epoll_fd, EPOLL_CTL_ADD, sender_fd, &event);
|
|
if (self->events && self->events->sender_state)
|
|
self->events->sender_state (self, sender_fd,
|
|
WPIPC_RECEIVER_SENDER_STATE_CONNECTED, self->events_data);
|
|
}
|
|
|
|
static void
|
|
other_event_received (struct epoll_thread *t, int fd, void *data)
|
|
{
|
|
struct wpipc_receiver *self = data;
|
|
|
|
/* sender sends a message, read it and reply */
|
|
ssize_t size = wpipc_socket_read (fd, &self->buffer_read, &self->buffer_size);
|
|
if (size < 0) {
|
|
wpipc_log_error ("receiver: could not read message: %s", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
if (size == 0) {
|
|
/* client disconnected */
|
|
epoll_ctl (t->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
|
|
close (fd);
|
|
if (self->events && self->events->sender_state)
|
|
self->events->sender_state (self, fd,
|
|
WPIPC_RECEIVER_SENDER_STATE_DISCONNECTED, self->events_data);
|
|
return;
|
|
}
|
|
|
|
/* reply */
|
|
if (!reply_message (self, fd, self->buffer_read, size))
|
|
wpipc_log_error ("receiver: could not reply message: %s", strerror(errno));
|
|
|
|
return;
|
|
}
|
|
|
|
/* API */
|
|
|
|
struct wpipc_receiver *
|
|
wpipc_receiver_new (const char *path,
|
|
size_t buffer_size,
|
|
const struct wpipc_receiver_events *events,
|
|
void *events_data,
|
|
size_t user_size)
|
|
{
|
|
struct wpipc_receiver *self;
|
|
int name_size;
|
|
|
|
/* check params */
|
|
if (path == NULL || buffer_size == 0)
|
|
return NULL;
|
|
|
|
unlink (path);
|
|
|
|
self = calloc (1, sizeof (struct wpipc_receiver) + user_size);
|
|
if (self == NULL)
|
|
return NULL;
|
|
|
|
self->socket_fd = -1;
|
|
|
|
/* set address */
|
|
self->addr.sun_family = AF_LOCAL;
|
|
name_size = snprintf(self->addr.sun_path, sizeof(self->addr.sun_path), "%s",
|
|
path) + 1;
|
|
if (name_size > (int) sizeof(self->addr.sun_path))
|
|
goto error;
|
|
|
|
/* create socket */
|
|
self->socket_fd =
|
|
socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
|
if (self->socket_fd < 0)
|
|
goto error;
|
|
|
|
/* bind socket */
|
|
if (bind (self->socket_fd, (struct sockaddr *)&self->addr,
|
|
sizeof(self->addr)) != 0)
|
|
goto error;
|
|
|
|
/* listen socket */
|
|
if (listen (self->socket_fd, MAX_SENDERS) != 0)
|
|
goto error;
|
|
|
|
/* alloc buffer read */
|
|
self->buffer_size = buffer_size;
|
|
self->buffer_read = calloc (buffer_size, sizeof (uint8_t));
|
|
if (self->buffer_read == NULL)
|
|
goto error;
|
|
|
|
/* init epoll thread */
|
|
if (!wpipc_epoll_thread_init (&self->epoll_thread, self->socket_fd,
|
|
socket_event_received, other_event_received, self))
|
|
goto error;
|
|
|
|
self->events = events;
|
|
self->events_data = events_data;
|
|
if (user_size > 0)
|
|
self->user_data = (void *)((uint8_t *)self + sizeof (struct wpipc_receiver));
|
|
|
|
return self;
|
|
|
|
error:
|
|
if (self->buffer_read)
|
|
free (self->buffer_read);
|
|
if (self->socket_fd != -1)
|
|
close (self->socket_fd);
|
|
free (self);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
wpipc_receiver_free (struct wpipc_receiver *self)
|
|
{
|
|
wpipc_receiver_stop (self);
|
|
|
|
wpipc_epoll_thread_destroy (&self->epoll_thread);
|
|
free (self->buffer_read);
|
|
close (self->socket_fd);
|
|
free (self);
|
|
}
|
|
|
|
bool
|
|
wpipc_receiver_start (struct wpipc_receiver *self)
|
|
{
|
|
if (wpipc_receiver_is_running (self))
|
|
return true;
|
|
|
|
self->thread_running = wpipc_epoll_thread_start (&self->epoll_thread);
|
|
return self->thread_running;
|
|
}
|
|
|
|
void
|
|
wpipc_receiver_stop (struct wpipc_receiver *self)
|
|
{
|
|
if (wpipc_receiver_is_running (self)) {
|
|
wpipc_epoll_thread_stop (&self->epoll_thread);
|
|
self->thread_running = false;
|
|
}
|
|
}
|
|
|
|
bool
|
|
wpipc_receiver_is_running (struct wpipc_receiver *self)
|
|
{
|
|
return self->thread_running;
|
|
}
|
|
|
|
void *
|
|
wpipc_receiver_get_user_data (struct wpipc_receiver *self)
|
|
{
|
|
return self->user_data;
|
|
}
|