diff options
Diffstat (limited to 'cras/src/server/cras_server.c')
-rw-r--r-- | cras/src/server/cras_server.c | 720 |
1 files changed, 0 insertions, 720 deletions
diff --git a/cras/src/server/cras_server.c b/cras/src/server/cras_server.c deleted file mode 100644 index 5f2ce632..00000000 --- a/cras/src/server/cras_server.c +++ /dev/null @@ -1,720 +0,0 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#define _GNU_SOURCE /* Needed for Linux socket credential passing. */ - -#ifdef CRAS_DBUS -#include <dbus/dbus.h> -#endif -#include <errno.h> -#include <poll.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/param.h> -#include <sys/select.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/un.h> -#include <syslog.h> -#include <unistd.h> - -#ifdef CRAS_DBUS -#include "cras_a2dp_endpoint.h" -#include "cras_bt_manager.h" -#include "cras_bt_device.h" -#include "cras_bt_player.h" -#include "cras_dbus.h" -#include "cras_dbus_control.h" -#include "cras_hfp_ag_profile.h" -#include "cras_telephony.h" -#endif -#include "cras_alert.h" -#include "cras_audio_thread_monitor.h" -#include "cras_config.h" -#include "cras_device_monitor.h" -#include "cras_hotword_handler.h" -#include "cras_iodev_list.h" -#include "cras_main_message.h" -#include "cras_messages.h" -#include "cras_metrics.h" -#include "cras_non_empty_audio_handler.h" -#include "cras_observer.h" -#include "cras_rclient.h" -#include "cras_server.h" -#include "cras_server_metrics.h" -#include "cras_system_state.h" -#include "cras_tm.h" -#include "cras_types.h" -#include "cras_udev.h" -#include "cras_util.h" -#include "cras_mix.h" -#include "utlist.h" - -/* Store a list of clients that are attached to the server. - * Members: - * id - Unique identifier for this client. - * fd - socket file descriptor used to communicate with client. - * ucred - Process, user, and group ID of the client. - * client - rclient to handle messages from this client. - * pollfd - Pointer to struct pollfd for this callback. - */ -struct attached_client { - size_t id; - int fd; - struct ucred ucred; - struct cras_rclient *client; - struct pollfd *pollfd; - struct attached_client *next, *prev; -}; - -/* Stores file descriptors to callback mappings for clients. Callback/fd/data - * args are registered by clients. When fd is ready, the callback will be - * called on the main server thread and the callback data will be passed back to - * it. This allows the use of the main server loop instead of spawning a thread - * to watch file descriptors. The client can then read or write the fd. - * Members: - * fd - The file descriptor passed to select. - * callback - The funciton to call when fd is ready. - * callback_data - Pointer passed to the callback. - * pollfd - Pointer to struct pollfd for this callback. - * events - The events to poll for. - */ -struct client_callback { - int select_fd; - void (*callback)(void *data, int revents); - void *callback_data; - struct pollfd *pollfd; - int deleted; - int events; - struct client_callback *prev, *next; -}; - -/* Stores callback function and argument data to be executed later. */ -struct system_task { - void (*callback)(void *); - void *callback_data; - struct system_task *next, *prev; -}; - -/* A structure wraps data related to server socket. */ -struct server_socket { - struct sockaddr_un addr; - int fd; - enum CRAS_CONNECTION_TYPE type; -}; - -/* Local server data. */ -struct server_data { - struct attached_client *clients_head; - size_t num_clients; - struct client_callback *client_callbacks; - struct system_task *system_tasks; - size_t num_client_callbacks; - size_t next_client_id; - struct server_socket server_sockets[CRAS_NUM_CONN_TYPE]; -} server_instance; - -/* Cleanup a given server_socket */ -static void server_socket_cleanup(struct server_socket *socket) -{ - if (socket && socket->fd >= 0) { - close(socket->fd); - socket->fd = -1; - unlink(socket->addr.sun_path); - } -} - -/* Remove a client from the list and destroy it. Calling rclient_destroy will - * also free all the streams owned by the client */ -static void remove_client(struct attached_client *client) -{ - close(client->fd); - DL_DELETE(server_instance.clients_head, client); - server_instance.num_clients--; - cras_rclient_destroy(client->client); - free(client); -} - -/* This is called when "select" indicates that the client has written data to - * the socket. Read out one message and pass it to the client message handler. - */ -static void handle_message_from_client(struct attached_client *client) -{ - uint8_t buf[CRAS_SERV_MAX_MSG_SIZE]; - int nread; - unsigned int num_fds = 2; - int fds[num_fds]; - - nread = cras_recv_with_fds(client->fd, buf, sizeof(buf), fds, &num_fds); - if (nread < 0) - goto read_error; - if (cras_rclient_buffer_from_client(client->client, buf, nread, fds, - num_fds) < 0) - goto read_error; - return; - -read_error: - for (int i = 0; i < num_fds; i++) - if (fds[i] >= 0) - close(fds[i]); - switch (nread) { - case 0: - break; - default: - syslog(LOG_DEBUG, "read err [%d] '%s', removing client %zu", - -nread, strerror(-nread), client->id); - break; - } - remove_client(client); -} - -/* Discovers and fills in info about the client that can be obtained from the - * socket. The pid of the attaching client identifies it in logs. */ -static void fill_client_info(struct attached_client *client) -{ - socklen_t ucred_length = sizeof(client->ucred); - - if (getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &client->ucred, - &ucred_length)) - syslog(LOG_INFO, "Failed to get client socket info\n"); -} - -/* Fills the server_state with the current list of attached clients. */ -static void send_client_list_to_clients(struct server_data *serv) -{ - struct attached_client *c; - struct cras_attached_client_info *info; - struct cras_server_state *state; - unsigned i; - - state = cras_system_state_update_begin(); - if (!state) - return; - - state->num_attached_clients = - MIN(CRAS_MAX_ATTACHED_CLIENTS, serv->num_clients); - - info = state->client_info; - i = 0; - DL_FOREACH (serv->clients_head, c) { - info->id = c->id; - info->pid = c->ucred.pid; - info->uid = c->ucred.uid; - info->gid = c->ucred.gid; - info++; - if (++i == CRAS_MAX_ATTACHED_CLIENTS) - break; - } - - cras_system_state_update_complete(); -} - -/* Handles requests from a client to attach to the server. Create a local - * structure to track the client, assign it a unique id and let it attach */ -static void handle_new_connection(struct server_socket *server_socket) -{ - int connection_fd; - struct attached_client *poll_client; - socklen_t address_length; - - poll_client = malloc(sizeof(struct attached_client)); - if (poll_client == NULL) { - syslog(LOG_ERR, "Allocating poll_client"); - return; - } - - memset(&address_length, 0, sizeof(address_length)); - connection_fd = accept(server_socket->fd, - (struct sockaddr *)&server_socket->addr, - &address_length); - if (connection_fd < 0) { - syslog(LOG_ERR, "connecting"); - free(poll_client); - return; - } - - /* find next available client id */ - while (1) { - struct attached_client *out; - DL_SEARCH_SCALAR(server_instance.clients_head, out, id, - server_instance.next_client_id); - poll_client->id = server_instance.next_client_id; - server_instance.next_client_id++; - if (out == NULL) - break; - } - - /* When full, getting an error is preferable to blocking. */ - cras_make_fd_nonblocking(connection_fd); - - poll_client->fd = connection_fd; - poll_client->next = NULL; - poll_client->pollfd = NULL; - fill_client_info(poll_client); - - poll_client->client = cras_rclient_create( - connection_fd, poll_client->id, server_socket->type); - if (poll_client->client == NULL) { - syslog(LOG_ERR, "failed to create client"); - goto error; - } - - DL_APPEND(server_instance.clients_head, poll_client); - server_instance.num_clients++; - /* Send a current list of available inputs and outputs. */ - cras_iodev_list_update_device_list(); - send_client_list_to_clients(&server_instance); - return; -error: - close(connection_fd); - free(poll_client); - return; -} - -/* Add a file descriptor to be passed to select in the main loop. This is - * registered with system state so that it is called when any client asks to - * have a callback triggered based on an fd being readable. */ -static int add_select_fd(int fd, void (*cb)(void *data, int events), - void *callback_data, int events, void *server_data) -{ - struct client_callback *new_cb; - struct client_callback *client_cb; - struct server_data *serv; - - serv = (struct server_data *)server_data; - if (serv == NULL) - return -EINVAL; - - /* Check if fd already exists. */ - DL_FOREACH (serv->client_callbacks, client_cb) - if (client_cb->select_fd == fd && !client_cb->deleted) - return -EEXIST; - - new_cb = (struct client_callback *)calloc(1, sizeof(*new_cb)); - if (new_cb == NULL) - return -ENOMEM; - - new_cb->select_fd = fd; - new_cb->callback = cb; - new_cb->callback_data = callback_data; - new_cb->deleted = 0; - new_cb->events = events; - new_cb->pollfd = NULL; - - DL_APPEND(serv->client_callbacks, new_cb); - server_instance.num_client_callbacks++; - return 0; -} - -/* Removes a file descriptor to be passed to select in the main loop. This is - * registered with system state so that it is called when any client asks to - * remove a callback added with add_select_fd. */ -static void rm_select_fd(int fd, void *server_data) -{ - struct server_data *serv; - struct client_callback *client_cb; - - serv = (struct server_data *)server_data; - if (serv == NULL) - return; - - DL_FOREACH (serv->client_callbacks, client_cb) - if (client_cb->select_fd == fd) - client_cb->deleted = 1; -} - -/* Creates a new task entry and append to system_tasks list, which will be - * executed in main loop later without wait time. - */ -static int add_task(void (*cb)(void *data), void *callback_data, - void *server_data) -{ - struct server_data *serv; - struct system_task *new_task; - - serv = (struct server_data *)server_data; - if (serv == NULL) - return -EINVAL; - - new_task = (struct system_task *)calloc(1, sizeof(*new_task)); - if (new_task == NULL) - return -ENOMEM; - - new_task->callback = cb; - new_task->callback_data = callback_data; - - DL_APPEND(serv->system_tasks, new_task); - return 0; -} - -/* Cleans up the file descriptor list removing items deleted during the main - * loop iteration. */ -static void cleanup_select_fds(void *server_data) -{ - struct server_data *serv; - struct client_callback *client_cb; - - serv = (struct server_data *)server_data; - if (serv == NULL) - return; - - DL_FOREACH (serv->client_callbacks, client_cb) - if (client_cb->deleted) { - DL_DELETE(serv->client_callbacks, client_cb); - server_instance.num_client_callbacks--; - free(client_cb); - } -} - -/* Checks that at least two outputs are present (one will be the "empty" - * default device. */ -void check_output_exists(struct cras_timer *t, void *data) -{ - if (cras_iodev_list_get_outputs(NULL) < 2) - cras_metrics_log_event(kNoCodecsFoundMetric); -} - -#if defined(__amd64__) -/* CPU detection - probaby best to move this elsewhere */ -static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, - unsigned int *edx, unsigned int op) -{ - // clang-format off - __asm__ __volatile__ ( - "cpuid" - : "=a" (*eax), - "=b" (*ebx), - "=c" (*ecx), - "=d" (*edx) - : "a" (op), "c" (0) - ); - // clang-format on -} - -static unsigned int cpu_x86_flags(void) -{ - unsigned int eax, ebx, ecx, edx, id; - unsigned int cpu_flags = 0; - - cpuid(&id, &ebx, &ecx, &edx, 0); - - if (id >= 1) { - cpuid(&eax, &ebx, &ecx, &edx, 1); - - if (ecx & (1 << 20)) - cpu_flags |= CPU_X86_SSE4_2; - - if (ecx & (1 << 28)) - cpu_flags |= CPU_X86_AVX; - - if (ecx & (1 << 12)) - cpu_flags |= CPU_X86_FMA; - } - - if (id >= 7) { - cpuid(&eax, &ebx, &ecx, &edx, 7); - - if (ebx & (1 << 5)) - cpu_flags |= CPU_X86_AVX2; - } - - return cpu_flags; -} -#endif - -int cpu_get_flags(void) -{ -#if defined(__amd64__) - return cpu_x86_flags(); -#endif - return 0; -} - -/* - * Exported Interface. - */ - -int cras_server_init() -{ - /* Log to syslog. */ - openlog("cras_server", LOG_PID, LOG_USER); - - server_instance.next_client_id = RESERVED_CLIENT_IDS; - - /* Initialize global observer. */ - cras_observer_server_init(); - - /* init mixer with CPU capabilities */ - cras_mix_init(cpu_get_flags()); - - /* Allow clients to register callbacks for file descriptors. - * add_select_fd and rm_select_fd will add and remove file descriptors - * from the list that are passed to select in the main loop below. */ - cras_system_set_select_handler(add_select_fd, rm_select_fd, - &server_instance); - cras_system_set_add_task_handler(add_task, &server_instance); - cras_main_message_init(); - - /* Initializes all server_sockets */ - for (int conn_type = 0; conn_type < CRAS_NUM_CONN_TYPE; conn_type++) { - server_instance.server_sockets[conn_type].fd = -1; - } - - return 0; -} - -/* - * Creates a server socket with given connection type and listens on it. - * The socket_file will be created under cras_config_get_system_socket_file_dir - * with permission=0770. The socket_fd will be listened with parameter - * backlog=5. - * - * Returns 0 on success and leaves the created fd and the address information - * in server_socket. - * When error occurs, the created fd will be closed and the file path will be - * unlinked and returns negative error code. - */ -static int create_and_listen_server_socket(enum CRAS_CONNECTION_TYPE conn_type, - struct server_socket *server_socket) -{ - int socket_fd = -1; - int rc = 0; - struct sockaddr_un *addr = &server_socket->addr; - - socket_fd = socket(PF_UNIX, SOCK_SEQPACKET, 0); - if (socket_fd < 0) { - syslog(LOG_ERR, "Main server socket failed."); - rc = socket_fd; - goto error; - } - - memset(addr, 0, sizeof(*addr)); - addr->sun_family = AF_UNIX; - rc = cras_fill_socket_path(conn_type, addr->sun_path); - if (rc < 0) - goto error; - unlink(addr->sun_path); - - /* Linux quirk: calling fchmod before bind, sets the permissions of the - * file created by bind, leaving no window for it to be modified. Start - * with very restricted permissions. */ - rc = fchmod(socket_fd, 0700); - if (rc < 0) - goto error; - - rc = bind(socket_fd, (struct sockaddr *)addr, - sizeof(struct sockaddr_un)); - if (rc < 0) { - syslog(LOG_ERR, "Bind to server socket failed."); - rc = -errno; - goto error; - } - - /* Let other members in our group play audio through this socket. */ - rc = chmod(addr->sun_path, 0770); - if (rc < 0) - goto error; - - if (listen(socket_fd, 5) != 0) { - syslog(LOG_ERR, "Listen on server socket failed."); - rc = -errno; - goto error; - } - - server_socket->fd = socket_fd; - server_socket->type = conn_type; - return 0; -error: - if (socket_fd >= 0) { - close(socket_fd); - unlink(addr->sun_path); - } - return rc; -} - -/* Cleans up all server_socket in server_instance */ -static void cleanup_server_sockets() -{ - for (int conn_type = 0; conn_type < CRAS_NUM_CONN_TYPE; conn_type++) { - server_socket_cleanup( - &server_instance.server_sockets[conn_type]); - } -} - -int cras_server_run(unsigned int profile_disable_mask) -{ - static const unsigned int OUTPUT_CHECK_MS = 5 * 1000; -#ifdef CRAS_DBUS - DBusConnection *dbus_conn; -#endif - int rc = 0; - struct attached_client *elm; - struct client_callback *client_cb; - struct system_task *tasks; - struct system_task *system_task; - struct cras_tm *tm; - struct timespec ts, *poll_timeout; - int timers_active; - struct pollfd *pollfds; - unsigned int pollfds_size = 32; - unsigned int num_pollfds, poll_size_needed; - - pollfds = malloc(sizeof(*pollfds) * pollfds_size); - - cras_udev_start_sound_subsystem_monitor(); -#ifdef CRAS_DBUS - cras_bt_device_start_monitor(); -#endif - - cras_server_metrics_init(); - - cras_device_monitor_init(); - - cras_hotword_handler_init(); - - cras_non_empty_audio_handler_init(); - - cras_audio_thread_monitor_init(); - -#ifdef CRAS_DBUS - dbus_threads_init_default(); - dbus_conn = cras_dbus_connect_system_bus(); - if (dbus_conn) { - cras_bt_start(dbus_conn); - if (!(profile_disable_mask & CRAS_SERVER_PROFILE_MASK_HFP)) - cras_hfp_ag_profile_create(dbus_conn); - if (!(profile_disable_mask & CRAS_SERVER_PROFILE_MASK_HSP)) - cras_hsp_ag_profile_create(dbus_conn); - cras_telephony_start(dbus_conn); - if (!(profile_disable_mask & CRAS_SERVER_PROFILE_MASK_A2DP)) - cras_a2dp_endpoint_create(dbus_conn); - cras_bt_player_create(dbus_conn); - cras_dbus_control_start(dbus_conn); - } -#endif - - for (int conn_type = 0; conn_type < CRAS_NUM_CONN_TYPE; conn_type++) { - rc = create_and_listen_server_socket( - conn_type, &server_instance.server_sockets[conn_type]); - if (rc < 0) - goto bail; - } - - tm = cras_system_state_get_tm(); - if (!tm) { - syslog(LOG_ERR, "Getting timer manager."); - rc = -ENOMEM; - goto bail; - } - - /* After a delay, make sure there is at least one real output device. */ - cras_tm_create_timer(tm, OUTPUT_CHECK_MS, check_output_exists, 0); - - /* Main server loop - client callbacks are run from this context. */ - while (1) { - poll_size_needed = CRAS_NUM_CONN_TYPE + - server_instance.num_clients + - server_instance.num_client_callbacks; - if (poll_size_needed > pollfds_size) { - pollfds_size = 2 * poll_size_needed; - pollfds = realloc(pollfds, - sizeof(*pollfds) * pollfds_size); - } - - for (int conn_type = 0; conn_type < CRAS_NUM_CONN_TYPE; - conn_type++) { - pollfds[conn_type].fd = - server_instance.server_sockets[conn_type].fd; - pollfds[conn_type].events = POLLIN; - } - num_pollfds = CRAS_NUM_CONN_TYPE; - - DL_FOREACH (server_instance.clients_head, elm) { - pollfds[num_pollfds].fd = elm->fd; - pollfds[num_pollfds].events = POLLIN; - elm->pollfd = &pollfds[num_pollfds]; - num_pollfds++; - } - DL_FOREACH (server_instance.client_callbacks, client_cb) { - if (client_cb->deleted) - continue; - pollfds[num_pollfds].fd = client_cb->select_fd; - pollfds[num_pollfds].events = client_cb->events; - client_cb->pollfd = &pollfds[num_pollfds]; - num_pollfds++; - } - - tasks = server_instance.system_tasks; - server_instance.system_tasks = NULL; - DL_FOREACH (tasks, system_task) { - system_task->callback(system_task->callback_data); - DL_DELETE(tasks, system_task); - free(system_task); - } - - timers_active = cras_tm_get_next_timeout(tm, &ts); - - /* - * If new client task has been scheduled, no need to wait - * for timeout, just do another loop to execute them. - */ - if (server_instance.system_tasks) - poll_timeout = NULL; - else - poll_timeout = timers_active ? &ts : NULL; - - rc = ppoll(pollfds, num_pollfds, poll_timeout, NULL); - if (rc < 0) - continue; - - cras_tm_call_callbacks(tm); - - /* Check for new connections. */ - for (int conn_type = 0; conn_type < CRAS_NUM_CONN_TYPE; - conn_type++) { - if (pollfds[conn_type].revents & POLLIN) - handle_new_connection( - &server_instance - .server_sockets[conn_type]); - } - - /* Check if there are messages pending for any clients. */ - DL_FOREACH (server_instance.clients_head, elm) - if (elm->pollfd && elm->pollfd->revents & POLLIN) - handle_message_from_client(elm); - /* Check any client-registered fd/callback pairs. */ - DL_FOREACH (server_instance.client_callbacks, client_cb) - if (!client_cb->deleted && client_cb->pollfd && - (client_cb->pollfd->revents & client_cb->events)) - client_cb->callback(client_cb->callback_data, - client_cb->pollfd->revents); - - cleanup_select_fds(&server_instance); - -#ifdef CRAS_DBUS - if (dbus_conn) - cras_dbus_dispatch(dbus_conn); -#endif - - cras_alert_process_all_pending_alerts(); - } - -bail: - cleanup_server_sockets(); - free(pollfds); - cras_observer_server_free(); - return rc; -} - -void cras_server_send_to_all_clients(const struct cras_client_message *msg) -{ - struct attached_client *client; - - DL_FOREACH (server_instance.clients_head, client) - cras_rclient_send_message(client->client, msg, NULL, 0); -} |