| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- /*
- *
- * Embedded Linux library
- *
- * Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <errno.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <limits.h>
- #include <signal.h>
- #include <sys/epoll.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include "signal.h"
- #include "queue.h"
- #include "log.h"
- #include "useful.h"
- #include "main.h"
- #include "main-private.h"
- #include "private.h"
- #include "timeout.h"
- /**
- * SECTION:main
- * @short_description: Main loop handling
- *
- * Main loop handling
- */
- #define MAX_EPOLL_EVENTS 10
- #define IDLE_FLAG_DISPATCHING 1
- #define IDLE_FLAG_DESTROYED 2
- #define WATCH_FLAG_DISPATCHING 1
- #define WATCH_FLAG_DESTROYED 2
- #define WATCHDOG_TRIGGER_FREQ 2
- static int epoll_fd;
- static bool epoll_running;
- static bool epoll_terminate;
- static int idle_id;
- static int notify_fd;
- static struct l_timeout *watchdog;
- static struct l_queue *idle_list;
- struct watch_data {
- int fd;
- uint32_t events;
- uint32_t flags;
- watch_event_cb_t callback;
- watch_destroy_cb_t destroy;
- void *user_data;
- };
- #define DEFAULT_WATCH_ENTRIES 128
- static unsigned int watch_entries;
- static struct watch_data **watch_list;
- struct idle_data {
- idle_event_cb_t callback;
- idle_destroy_cb_t destroy;
- void *user_data;
- uint32_t flags;
- int id;
- };
- static inline bool __attribute__ ((always_inline)) create_epoll(void)
- {
- unsigned int i;
- epoll_fd = epoll_create1(EPOLL_CLOEXEC);
- if (epoll_fd < 0) {
- epoll_fd = 0;
- return false;
- }
- watch_list = malloc(DEFAULT_WATCH_ENTRIES * sizeof(void *));
- if (!watch_list)
- goto close_epoll;
- idle_list = l_queue_new();
- idle_id = 0;
- watch_entries = DEFAULT_WATCH_ENTRIES;
- for (i = 0; i < watch_entries; i++)
- watch_list[i] = NULL;
- return true;
- close_epoll:
- close(epoll_fd);
- epoll_fd = 0;
- return false;
- }
- int watch_add(int fd, uint32_t events, watch_event_cb_t callback,
- void *user_data, watch_destroy_cb_t destroy)
- {
- struct watch_data *data;
- struct epoll_event ev;
- int err;
- if (unlikely(fd < 0 || !callback))
- return -EINVAL;
- if (!epoll_fd)
- return -EIO;
- if ((unsigned int) fd > watch_entries - 1)
- return -ERANGE;
- data = l_new(struct watch_data, 1);
- data->fd = fd;
- data->events = events;
- data->flags = 0;
- data->callback = callback;
- data->destroy = destroy;
- data->user_data = user_data;
- memset(&ev, 0, sizeof(ev));
- ev.events = events;
- ev.data.ptr = data;
- err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev);
- if (err < 0) {
- l_free(data);
- return -errno;
- }
- watch_list[fd] = data;
- return 0;
- }
- int watch_modify(int fd, uint32_t events, bool force)
- {
- struct watch_data *data;
- struct epoll_event ev;
- int err;
- if (unlikely(fd < 0))
- return -EINVAL;
- if ((unsigned int) fd > watch_entries - 1)
- return -ERANGE;
- data = watch_list[fd];
- if (!data)
- return -ENXIO;
- if (data->events == events && !force)
- return 0;
- memset(&ev, 0, sizeof(ev));
- ev.events = events;
- ev.data.ptr = data;
- err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev);
- if (err < 0)
- return -errno;
- data->events = events;
- return 0;
- }
- int watch_clear(int fd)
- {
- struct watch_data *data;
- if (unlikely(fd < 0))
- return -EINVAL;
- if ((unsigned int) fd > watch_entries - 1)
- return -ERANGE;
- data = watch_list[fd];
- if (!data)
- return -ENXIO;
- watch_list[fd] = NULL;
- if (data->destroy)
- data->destroy(data->user_data);
- if (data->flags & WATCH_FLAG_DISPATCHING)
- data->flags |= WATCH_FLAG_DESTROYED;
- else
- l_free(data);
- return 0;
- }
- int watch_remove(int fd, bool epoll_del)
- {
- int err = watch_clear(fd);
- if (err < 0)
- return err;
- if (!epoll_del)
- goto done;
- err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
- if (err < 0)
- return -errno;
- done:
- return err;
- }
- static bool idle_remove_by_id(void *data, void *user_data)
- {
- struct idle_data *idle = data;
- int id = L_PTR_TO_INT(user_data);
- if (idle->id != id)
- return false;
- if (idle->destroy)
- idle->destroy(idle->user_data);
- if (idle->flags & IDLE_FLAG_DISPATCHING) {
- idle->flags |= IDLE_FLAG_DESTROYED;
- return false;
- }
- l_free(idle);
- return true;
- }
- static bool idle_prune(void *data, void *user_data)
- {
- struct idle_data *idle = data;
- if ((idle->flags & IDLE_FLAG_DESTROYED) == 0)
- return false;
- l_free(idle);
- return true;
- }
- int idle_add(idle_event_cb_t callback, void *user_data, uint32_t flags,
- idle_destroy_cb_t destroy)
- {
- struct idle_data *data;
- if (unlikely(!callback))
- return -EINVAL;
- if (!epoll_fd)
- return -EIO;
- data = l_new(struct idle_data, 1);
- data->callback = callback;
- data->destroy = destroy;
- data->user_data = user_data;
- data->flags = flags;
- if (!l_queue_push_tail(idle_list, data)) {
- l_free(data);
- return -ENOMEM;
- }
- data->id = idle_id++;
- if (idle_id == INT_MAX)
- idle_id = 0;
- return data->id;
- }
- void idle_remove(int id)
- {
- l_queue_foreach_remove(idle_list, idle_remove_by_id,
- L_INT_TO_PTR(id));
- }
- static void idle_destroy(void *data)
- {
- struct idle_data *idle = data;
- if (!(idle->flags & IDLE_FLAG_NO_WARN_DANGLING))
- l_error("Dangling idle descriptor %p, %d found",
- data, idle->id);
- if (idle->destroy)
- idle->destroy(idle->user_data);
- l_free(idle);
- }
- static void idle_dispatch(void *data, void *user_data)
- {
- struct idle_data *idle = data;
- if (!idle->callback)
- return;
- idle->flags |= IDLE_FLAG_DISPATCHING;
- idle->callback(idle->user_data);
- idle->flags &= ~IDLE_FLAG_DISPATCHING;
- }
- static int sd_notify(const char *state)
- {
- int err;
- if (notify_fd <= 0)
- return -ENOTCONN;
- err = send(notify_fd, state, strlen(state), MSG_NOSIGNAL);
- if (err < 0)
- return -errno;
- return 0;
- }
- static void watchdog_callback(struct l_timeout *timeout, void *user_data)
- {
- int msec = L_PTR_TO_INT(user_data);
- sd_notify("WATCHDOG=1");
- l_timeout_modify_ms(timeout, msec);
- }
- static void create_sd_notify_socket(void)
- {
- const char *sock;
- struct sockaddr_un addr;
- const char *watchdog_usec;
- int msec;
- /* check if NOTIFY_SOCKET has been set */
- sock = getenv("NOTIFY_SOCKET");
- if (!sock)
- return;
- /* check for abstract socket or absolute path */
- if (sock[0] != '@' && sock[0] != '/')
- return;
- notify_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (notify_fd < 0) {
- notify_fd = 0;
- return;
- }
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, sock, sizeof(addr.sun_path) - 1);
- if (addr.sun_path[0] == '@')
- addr.sun_path[0] = '\0';
- if (bind(notify_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(notify_fd);
- notify_fd = 0;
- return;
- }
- watchdog_usec = getenv("WATCHDOG_USEC");
- if (!watchdog_usec)
- return;
- msec = atoi(watchdog_usec) / 1000;
- if (msec < WATCHDOG_TRIGGER_FREQ)
- return;
- msec /= WATCHDOG_TRIGGER_FREQ;
- watchdog = l_timeout_create_ms(msec, watchdog_callback,
- L_INT_TO_PTR(msec), NULL);
- }
- /**
- * l_main_init:
- *
- * Initialize the main loop. This must be called before l_main_run()
- * and any other function that directly or indirectly sets up an idle
- * or watch. A safe rule-of-thumb is to call it before any function
- * prefixed with "l_".
- *
- * Returns: true if initialization was successful, false otherwise.
- **/
- LIB_EXPORT bool l_main_init(void)
- {
- if (unlikely(epoll_running))
- return false;
- if (!create_epoll())
- return false;
- create_sd_notify_socket();
- epoll_terminate = false;
- return true;
- }
- /**
- * l_main_prepare:
- *
- * Prepare the iteration of the main loop
- *
- * Returns: The timeout to use. This will be 0 if idle-event processing is
- * currently pending, or -1 otherwise. This value can be used to pass to
- * l_main_iterate.
- */
- LIB_EXPORT int l_main_prepare(void)
- {
- return l_queue_isempty(idle_list) ? -1 : 0;
- }
- /**
- * l_main_iterate:
- *
- * Run one iteration of the main event loop
- */
- LIB_EXPORT void l_main_iterate(int timeout)
- {
- struct epoll_event events[MAX_EPOLL_EVENTS];
- struct watch_data *data;
- int n, nfds;
- nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout);
- for (n = 0; n < nfds; n++) {
- data = events[n].data.ptr;
- data->flags |= WATCH_FLAG_DISPATCHING;
- }
- for (n = 0; n < nfds; n++) {
- data = events[n].data.ptr;
- if (data->flags & WATCH_FLAG_DESTROYED)
- continue;
- data->callback(data->fd, events[n].events,
- data->user_data);
- }
- for (n = 0; n < nfds; n++) {
- data = events[n].data.ptr;
- if (data->flags & WATCH_FLAG_DESTROYED)
- l_free(data);
- else
- data->flags = 0;
- }
- l_queue_foreach(idle_list, idle_dispatch, NULL);
- l_queue_foreach_remove(idle_list, idle_prune, NULL);
- }
- /**
- * l_main_run:
- *
- * Run the main loop
- *
- * The loop may be restarted by invoking this function after a
- * previous invocation returns, provided that l_main_exit() has not
- * been called first.
- *
- * Returns: #EXIT_SUCCESS after successful execution or #EXIT_FAILURE in
- * case of failure
- **/
- LIB_EXPORT int l_main_run(void)
- {
- int timeout;
- /* Has l_main_init() been called? */
- if (unlikely(!epoll_fd))
- return EXIT_FAILURE;
- if (unlikely(epoll_running))
- return EXIT_FAILURE;
- epoll_running = true;
- for (;;) {
- if (epoll_terminate)
- break;
- timeout = l_main_prepare();
- l_main_iterate(timeout);
- }
- epoll_running = false;
- if (notify_fd) {
- close(notify_fd);
- notify_fd = 0;
- l_timeout_remove(watchdog);
- watchdog = NULL;
- }
- return EXIT_SUCCESS;
- }
- /**
- * l_main_exit:
- *
- * Clean up after main loop completes.
- *
- **/
- LIB_EXPORT bool l_main_exit(void)
- {
- unsigned int i;
- if (epoll_running) {
- l_error("Cleanup attempted on running main loop");
- return false;
- }
- for (i = 0; i < watch_entries; i++) {
- struct watch_data *data = watch_list[i];
- if (!data)
- continue;
- epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
- if (data->destroy)
- data->destroy(data->user_data);
- else
- l_error("Dangling file descriptor %d found", data->fd);
- l_free(data);
- }
- watch_entries = 0;
- free(watch_list);
- watch_list = NULL;
- l_queue_destroy(idle_list, idle_destroy);
- idle_list = NULL;
- close(epoll_fd);
- epoll_fd = 0;
- return true;
- }
- /**
- * l_main_quit:
- *
- * Teminate the running main loop
- *
- * Returns: #true when terminating the main loop or #false in case of failure
- **/
- LIB_EXPORT bool l_main_quit(void)
- {
- if (unlikely(!epoll_running))
- return false;
- epoll_terminate = true;
- return true;
- }
- struct signal_data {
- l_main_signal_cb_t callback;
- void *user_data;
- };
- static void sigint_handler(void *user_data)
- {
- struct signal_data *data = user_data;
- if (data->callback)
- data->callback(SIGINT, data->user_data);
- }
- static void sigterm_handler(void *user_data)
- {
- struct signal_data *data = user_data;
- if (data->callback)
- data->callback(SIGTERM, data->user_data);
- }
- /**
- * l_main_run_with_signal:
- *
- * Run the main loop with signal handling for SIGINT and SIGTERM
- *
- * Returns: #EXIT_SUCCESS after successful execution or #EXIT_FAILURE in
- * case of failure
- **/
- LIB_EXPORT int l_main_run_with_signal(l_main_signal_cb_t callback,
- void *user_data)
- {
- struct signal_data *data;
- struct l_signal *sigint;
- struct l_signal *sigterm;
- int result;
- data = l_new(struct signal_data, 1);
- data->callback = callback;
- data->user_data = user_data;
- sigint = l_signal_create(SIGINT, sigint_handler, data, NULL);
- sigterm = l_signal_create(SIGTERM, sigterm_handler, data, NULL);
- result = l_main_run();
- l_signal_remove(sigint);
- l_signal_remove(sigterm);
- l_free(data);
- return result;
- }
- /**
- * l_main_get_epoll_fd:
- *
- * Can be used to obtain the epoll file descriptor in order to integrate
- * the ell main event loop with other event loops.
- *
- * Returns: epoll file descriptor
- **/
- LIB_EXPORT int l_main_get_epoll_fd(void)
- {
- return epoll_fd;
- }
|