/daemon/sock-serv.c
C | 297 lines | 240 code | 34 blank | 23 comment | 47 complexity | 67b6499728d3c104229fea0a53573f6f MD5 | raw file
- /*
- * This file is part of mradio.
- *
- * Copyright (C) 2014-2016 Karl Linden <karl.j.linden@gmail.com>
- *
- * 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 3 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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- *
- */
- /* This must be set before any #includes. */
- #define MRADIO_LOG_DOMAIN MRADIO_LOG_DOMAIN_SERV
- #if HAVE_CONFIG_H
- # include <config.h>
- #endif /* HAVE_CONFIG_H */
- #include <errno.h>
- #include <stdbool.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <sys/un.h>
- #include <unistd.h>
- #include <mradio/common.h>
- #include <mradio/info.h>
- #include <mradio/log.h>
- #include <mradio/mstr.h>
- #include <mradio/sock.h>
- #include <common/massert.h>
- #include "ml.h"
- #include "nonblock.h"
- #include "pipe-serv.h"
- #include "serv.h"
- #include "sock-serv.h"
- #define SOCK_SERV_LISTEN_BACKLOG 16
- typedef struct sock_serv_conn_s sock_serv_conn_t;
- typedef struct sock_serv_s sock_serv_t;
- struct sock_serv_conn_s
- {
- sock_serv_t * sockserv;
- serv_t * pipeserv;
- sock_serv_conn_t * prev;
- sock_serv_conn_t * next;
- };
- struct sock_serv_s {
- char * unix; /* path if type unix, NULL else */
- char * password; /* password or NULL */ /* mstr */
- int fd;
- int ml_fd;
- sock_serv_conn_t * conns; /* linked list of open connections */
- };
- static void
- sock_serv_connfd_close(int connfd)
- {
- if (shutdown(connfd, SHUT_RDWR)) {
- log_error_errno("could not shutdown connection");
- }
- if (close(connfd)) {
- log_error_errno("could not close connection");
- }
- return;
- }
- static void NONNULL
- sock_serv_conn_link(sock_serv_t * sockserv,
- sock_serv_conn_t * conn)
- {
- sock_serv_conn_t * next = sockserv->conns;
- conn->prev = NULL;
- conn->next = next;
- if (next != NULL)
- {
- next->prev = conn;
- }
- sockserv->conns = conn;
- return;
- }
- static void NONNULL
- sock_serv_conn_unlink(sock_serv_conn_t * conn)
- {
- sock_serv_t * sockserv = conn->sockserv;
- sock_serv_conn_t * prev = conn->prev;
- sock_serv_conn_t * next = conn->next;
- if (prev != NULL)
- {
- prev->next = next;
- }
- else
- {
- massert(sockserv->conns == conn);
- sockserv->conns = next;
- }
- if (next != NULL)
- {
- next->prev = prev;
- }
- return;
- }
- static void NONNULL
- sock_serv_conn_stop(serv_t * pipeserv)
- {
- sock_serv_conn_t * conn = pipe_serv_data(pipeserv);
- int connfd = pipe_serv_rdfd(pipeserv);
- sock_serv_conn_unlink(conn);
- sock_serv_connfd_close(connfd);
- return;
- }
- static unsigned NONNULL
- sock_serv_accept(int ml_fd)
- {
- sock_serv_t * sockserv = ml_fd_get_data(ml_fd);
- int connfd;
- serv_t * pipeserv;
- sock_serv_conn_t * conn;
- while ((connfd = accept(sockserv->fd, NULL, NULL)) < 0 &&
- (errno == ECONNABORTED || errno == EINTR))
- {
- /* empty */;
- }
- if (connfd < 0)
- {
- if (errno != EAGAIN && errno != EWOULDBLOCK)
- {
- log_error_errno("could not accept connection");
- }
- return 0;
- }
- pipeserv = pipe_serv_start(NULL, sockserv->password, connfd, connfd,
- sizeof(sock_serv_conn_t),
- &sock_serv_conn_stop,
- PIPE_SERV_STOP_ALL);
- if (pipeserv == NULL)
- {
- sock_serv_connfd_close(connfd);
- return 1;
- }
- /* Initialize connection structure. */
- conn = pipe_serv_data(pipeserv);
- conn->sockserv = sockserv;
- conn->pipeserv = pipeserv;
- sock_serv_conn_link(sockserv, conn);
- return 0;
- }
- serv_t * NONNULL
- sock_serv_start(const info_t * info)
- {
- sockinfo_t sockinfo;
- char * name = info_get_name(info); /* mstr */
- serv_t * serv = NULL;
- sock_serv_t * sockserv = NULL;
- bool bound = false;
- if (sockinfo_fill(&sockinfo, info))
- {
- goto error;
- }
- serv = serv_new(name, SERV_SOCK, sizeof(sock_serv_t));
- if (serv == NULL)
- {
- goto error;
- }
- mstr_unref(name);
- sockserv = serv_data(serv);
- sockserv->unix = NULL;
- sockserv->password = info_get_password(info);
- sockserv->fd = -1;
- sockserv->ml_fd = -1;
- sockserv->conns = NULL;
- if (sockinfo.sockaddr.addr.sa_family == AF_UNIX)
- {
- sockserv->unix = strdup(sockinfo.sockaddr.un.sun_path);
- if (sockserv->unix == NULL)
- {
- log_error_errno("could not allocate memory");
- goto error;
- }
- }
- sockserv->fd = socket(sockinfo.sockaddr.addr.sa_family, SOCK_STREAM,
- 0);
- if (sockserv->fd < 0) {
- log_fatal_errno("could not create socket");
- goto error;
- }
- if (nonblock(sockserv->fd)) {
- goto error;
- }
- if (bind(sockserv->fd, &sockinfo.sockaddr.addr, sockinfo.size)) {
- log_fatal_errno("could not bind socket");
- goto error;
- }
- bound = true;
- if (listen(sockserv->fd, SOCK_SERV_LISTEN_BACKLOG)) {
- log_fatal_errno("could not listen on control socket");
- goto error;
- }
- sockserv->ml_fd = ml_fd_new(sockserv->fd, sockserv);
- if (sockserv->ml_fd < 0) {
- log_fatal("could not create main loop file descriptor");
- goto error;
- }
- ml_fd_read_cb(sockserv->ml_fd, &sock_serv_accept);
- return serv;
- error:
- COLD;
- if (serv != NULL)
- {
- if (sockserv->ml_fd >= 0)
- {
- ml_fd_destroy(sockserv->ml_fd);
- }
- if (sockserv->fd >= 0 && close(sockserv->fd))
- {
- log_error_errno("could not close socket");
- }
- if (bound && sockserv->unix != NULL && unlink(sockserv->unix))
- {
- log_error_errno("could not unlink socket");
- }
- free(sockserv->unix);
- mstr_unref(sockserv->password);
- serv_destroy(serv);
- }
- return NULL;
- }
- void NONNULL
- sock_serv_stop(serv_t * serv)
- {
- sock_serv_t * sockserv;
- sock_serv_conn_t * conn;
- sockserv = serv_data(serv);
- while ((conn = sockserv->conns) != NULL)
- {
- pipe_serv_stop(conn->pipeserv);
- }
- ml_fd_destroy(sockserv->ml_fd);
- if (close(sockserv->fd))
- {
- log_error_errno("could not close socket");
- }
- if (sockserv->unix != NULL)
- {
- if (unlink(sockserv->unix))
- {
- log_error_errno("could not unlink socket");
- }
- free(sockserv->unix);
- }
- mstr_unref(sockserv->password);
- serv_destroy(serv);
- return;
- }