summaryrefslogtreecommitdiffstats
path: root/spa/plugins/support/system.c
diff options
context:
space:
mode:
Diffstat (limited to 'spa/plugins/support/system.c')
-rw-r--r--spa/plugins/support/system.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/spa/plugins/support/system.c b/spa/plugins/support/system.c
new file mode 100644
index 0000000..e7efec9
--- /dev/null
+++ b/spa/plugins/support/system.c
@@ -0,0 +1,384 @@
+/* Spa
+ *
+ * Copyright © 2019 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/timerfd.h>
+#include <sys/eventfd.h>
+#include <sys/signalfd.h>
+
+#include <spa/support/log.h>
+#include <spa/support/system.h>
+#include <spa/support/plugin.h>
+#include <spa/utils/type.h>
+#include <spa/utils/names.h>
+#include <spa/utils/string.h>
+
+static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.system");
+#undef SPA_LOG_TOPIC_DEFAULT
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
+
+#ifndef TFD_TIMER_CANCEL_ON_SET
+# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+#endif
+
+struct impl {
+ struct spa_handle handle;
+ struct spa_system system;
+ struct spa_log *log;
+};
+
+static ssize_t impl_read(void *object, int fd, void *buf, size_t count)
+{
+ ssize_t res = read(fd, buf, count);
+ return res < 0 ? -errno : res;
+}
+
+static ssize_t impl_write(void *object, int fd, const void *buf, size_t count)
+{
+ ssize_t res = write(fd, buf, count);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_ioctl(void *object, int fd, unsigned long request, ...)
+{
+ int res;
+ va_list ap;
+ long arg;
+
+ va_start(ap, request);
+ arg = va_arg(ap, long);
+ res = ioctl(fd, request, arg);
+ va_end(ap);
+
+ return res < 0 ? -errno : res;
+}
+
+static int impl_close(void *object, int fd)
+{
+ struct impl *impl = object;
+ int res = close(fd);
+ spa_log_debug(impl->log, "%p: close fd:%d", impl, fd);
+ return res < 0 ? -errno : res;
+}
+
+/* clock */
+static int impl_clock_gettime(void *object,
+ int clockid, struct timespec *value)
+{
+ int res = clock_gettime(clockid, value);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_clock_getres(void *object,
+ int clockid, struct timespec *res)
+{
+ int r = clock_getres(clockid, res);
+ return r < 0 ? -errno : r;
+}
+
+/* poll */
+static int impl_pollfd_create(void *object, int flags)
+{
+ struct impl *impl = object;
+ int fl = 0, res;
+ if (flags & SPA_FD_CLOEXEC)
+ fl |= EPOLL_CLOEXEC;
+ res = epoll_create1(fl);
+ spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_pollfd_add(void *object, int pfd, int fd, uint32_t events, void *data)
+{
+ struct epoll_event ep;
+ int res;
+
+ spa_zero(ep);
+ ep.events = events;
+ ep.data.ptr = data;
+
+ res = epoll_ctl(pfd, EPOLL_CTL_ADD, fd, &ep);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_pollfd_mod(void *object, int pfd, int fd, uint32_t events, void *data)
+{
+ struct epoll_event ep;
+ int res;
+
+ spa_zero(ep);
+ ep.events = events;
+ ep.data.ptr = data;
+
+ res = epoll_ctl(pfd, EPOLL_CTL_MOD, fd, &ep);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_pollfd_del(void *object, int pfd, int fd)
+{
+ int res = epoll_ctl(pfd, EPOLL_CTL_DEL, fd, NULL);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_pollfd_wait(void *object, int pfd,
+ struct spa_poll_event *ev, int n_ev, int timeout)
+{
+ struct epoll_event ep[n_ev];
+ int i, nfds;
+
+ if (SPA_UNLIKELY((nfds = epoll_wait(pfd, ep, n_ev, timeout)) < 0))
+ return -errno;
+
+ for (i = 0; i < nfds; i++) {
+ ev[i].events = ep[i].events;
+ ev[i].data = ep[i].data.ptr;
+ }
+ return nfds;
+}
+
+/* timers */
+static int impl_timerfd_create(void *object, int clockid, int flags)
+{
+ struct impl *impl = object;
+ int fl = 0, res;
+ if (flags & SPA_FD_CLOEXEC)
+ fl |= TFD_CLOEXEC;
+ if (flags & SPA_FD_NONBLOCK)
+ fl |= TFD_NONBLOCK;
+ res = timerfd_create(clockid, fl);
+ spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_timerfd_settime(void *object,
+ int fd, int flags,
+ const struct itimerspec *new_value,
+ struct itimerspec *old_value)
+{
+ int fl = 0, res;
+ if (flags & SPA_FD_TIMER_ABSTIME)
+ fl |= TFD_TIMER_ABSTIME;
+ if (flags & SPA_FD_TIMER_CANCEL_ON_SET)
+ fl |= TFD_TIMER_CANCEL_ON_SET;
+ res = timerfd_settime(fd, fl, new_value, old_value);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_timerfd_gettime(void *object,
+ int fd, struct itimerspec *curr_value)
+{
+ int res = timerfd_gettime(fd, curr_value);
+ return res < 0 ? -errno : res;
+
+}
+static int impl_timerfd_read(void *object, int fd, uint64_t *expirations)
+{
+ if (read(fd, expirations, sizeof(uint64_t)) != sizeof(uint64_t))
+ return -errno;
+ return 0;
+}
+
+/* events */
+static int impl_eventfd_create(void *object, int flags)
+{
+ struct impl *impl = object;
+ int fl = 0, res;
+ if (flags & SPA_FD_CLOEXEC)
+ fl |= EFD_CLOEXEC;
+ if (flags & SPA_FD_NONBLOCK)
+ fl |= EFD_NONBLOCK;
+ if (flags & SPA_FD_EVENT_SEMAPHORE)
+ fl |= EFD_SEMAPHORE;
+ res = eventfd(0, fl);
+ spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
+ return res < 0 ? -errno : res;
+}
+
+static int impl_eventfd_write(void *object, int fd, uint64_t count)
+{
+ if (write(fd, &count, sizeof(uint64_t)) != sizeof(uint64_t))
+ return -errno;
+ return 0;
+}
+
+static int impl_eventfd_read(void *object, int fd, uint64_t *count)
+{
+ if (read(fd, count, sizeof(uint64_t)) != sizeof(uint64_t))
+ return -errno;
+ return 0;
+}
+
+/* signals */
+static int impl_signalfd_create(void *object, int signal, int flags)
+{
+ struct impl *impl = object;
+ sigset_t mask;
+ int res, fl = 0;
+
+ if (flags & SPA_FD_CLOEXEC)
+ fl |= SFD_CLOEXEC;
+ if (flags & SPA_FD_NONBLOCK)
+ fl |= SFD_NONBLOCK;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, signal);
+ res = signalfd(-1, &mask, fl);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+ spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
+
+ return res < 0 ? -errno : res;
+}
+
+static int impl_signalfd_read(void *object, int fd, int *signal)
+{
+ struct signalfd_siginfo signal_info;
+ int len;
+
+ len = read(fd, &signal_info, sizeof signal_info);
+ if (!(len == -1 && errno == EAGAIN) && len != sizeof signal_info)
+ return -errno;
+
+ *signal = signal_info.ssi_signo;
+
+ return 0;
+}
+
+static const struct spa_system_methods impl_system = {
+ SPA_VERSION_SYSTEM_METHODS,
+ .read = impl_read,
+ .write = impl_write,
+ .ioctl = impl_ioctl,
+ .close = impl_close,
+ .clock_gettime = impl_clock_gettime,
+ .clock_getres = impl_clock_getres,
+ .pollfd_create = impl_pollfd_create,
+ .pollfd_add = impl_pollfd_add,
+ .pollfd_mod = impl_pollfd_mod,
+ .pollfd_del = impl_pollfd_del,
+ .pollfd_wait = impl_pollfd_wait,
+ .timerfd_create = impl_timerfd_create,
+ .timerfd_settime = impl_timerfd_settime,
+ .timerfd_gettime = impl_timerfd_gettime,
+ .timerfd_read = impl_timerfd_read,
+ .eventfd_create = impl_eventfd_create,
+ .eventfd_write = impl_eventfd_write,
+ .eventfd_read = impl_eventfd_read,
+ .signalfd_create = impl_signalfd_create,
+ .signalfd_read = impl_signalfd_read,
+};
+
+static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
+{
+ struct impl *impl;
+
+ spa_return_val_if_fail(handle != NULL, -EINVAL);
+ spa_return_val_if_fail(interface != NULL, -EINVAL);
+
+ impl = (struct impl *) handle;
+
+ if (spa_streq(type, SPA_TYPE_INTERFACE_System))
+ *interface = &impl->system;
+ else
+ return -ENOENT;
+
+ return 0;
+}
+
+static int impl_clear(struct spa_handle *handle)
+{
+ spa_return_val_if_fail(handle != NULL, -EINVAL);
+ return 0;
+}
+
+static size_t
+impl_get_size(const struct spa_handle_factory *factory,
+ const struct spa_dict *params)
+{
+ return sizeof(struct impl);
+}
+
+static int
+impl_init(const struct spa_handle_factory *factory,
+ struct spa_handle *handle,
+ const struct spa_dict *info,
+ const struct spa_support *support,
+ uint32_t n_support)
+{
+ struct impl *impl;
+
+ spa_return_val_if_fail(factory != NULL, -EINVAL);
+ spa_return_val_if_fail(handle != NULL, -EINVAL);
+
+ handle->get_interface = impl_get_interface;
+ handle->clear = impl_clear;
+
+ impl = (struct impl *) handle;
+ impl->system.iface = SPA_INTERFACE_INIT(
+ SPA_TYPE_INTERFACE_System,
+ SPA_VERSION_SYSTEM,
+ &impl_system, impl);
+
+ impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
+ spa_log_topic_init(impl->log, &log_topic);
+
+ spa_log_debug(impl->log, "%p: initialized", impl);
+
+ return 0;
+}
+
+static const struct spa_interface_info impl_interfaces[] = {
+ {SPA_TYPE_INTERFACE_System,},
+};
+
+static int
+impl_enum_interface_info(const struct spa_handle_factory *factory,
+ const struct spa_interface_info **info,
+ uint32_t *index)
+{
+ spa_return_val_if_fail(factory != NULL, -EINVAL);
+ spa_return_val_if_fail(info != NULL, -EINVAL);
+ spa_return_val_if_fail(index != NULL, -EINVAL);
+
+ if (*index >= SPA_N_ELEMENTS(impl_interfaces))
+ return 0;
+
+ *info = &impl_interfaces[(*index)++];
+ return 1;
+}
+
+const struct spa_handle_factory spa_support_system_factory = {
+ SPA_VERSION_HANDLE_FACTORY,
+ SPA_NAME_SUPPORT_SYSTEM,
+ NULL,
+ impl_get_size,
+ impl_init,
+ impl_enum_interface_info
+};