systemd: integrate sd-event into glib main loop
authorThomas Haller <thaller@redhat.com>
Wed, 9 Mar 2016 11:27:56 +0000 (12:27 +0100)
committerThomas Haller <thaller@redhat.com>
Fri, 11 Mar 2016 08:28:12 +0000 (09:28 +0100)
We should not try to reimplement sd-event based on glib,
instead we should hook sd-event into the glib mainloop.

configure.ac
src/Makefile.am
src/devices/tests/test-lldp.c
src/main.c
src/nm-iface-helper.c
src/systemd/nm-sd-adapt.c
src/systemd/nm-sd-adapt.h
src/systemd/src/libsystemd/sd-event/sd-event.c
src/systemd/src/libsystemd/sd-id128/sd-id128.c
src/tests/Makefile.am
src/tests/test-systemd.c

index f945c6f..780f007 100644 (file)
@@ -86,6 +86,9 @@ AC_SUBST(nmdatadir, '${datadir}'/$PACKAGE, [NetworkManager shared data directory
 AC_SUBST(nmstatedir, '${localstatedir}'/lib/$PACKAGE, [NetworkManager persistent state directory])
 AC_SUBST(nmrundir, '${runstatedir}'/$PACKAGE, [NetworkManager runtime state directory])
 
+AC_GNU_SOURCE
+AC_CHECK_FUNCS([__secure_getenv secure_getenv])
+
 # Alternative configuration plugins
 AC_ARG_ENABLE(config-plugin-ibft, AS_HELP_STRING([--enable-config-plugin-ibft], [enable ibft configuration plugin]))
 AC_ARG_ENABLE(ifcfg-rh, AS_HELP_STRING([--enable-ifcfg-rh], [enable ifcfg-rh configuration plugin (Fedora/RHEL)]))
index 34483b5..f7f2e73 100644 (file)
@@ -156,6 +156,7 @@ libsystemd_nm_la_SOURCES = \
        systemd/src/libsystemd-network/sd-ipv4acd.c \
        systemd/src/libsystemd-network/sd-ipv4ll.c \
        systemd/src/libsystemd-network/sd-lldp.c \
+       systemd/src/libsystemd/sd-event/sd-event.c \
        systemd/src/libsystemd/sd-id128/sd-id128.c \
        systemd/src/shared/dns-domain.c \
        systemd/src/shared/dns-domain.h \
index 80ba725..2af46ab 100644 (file)
@@ -27,6 +27,7 @@
 #include <sys/types.h>
 
 #include "nm-lldp-listener.h"
+#include "nm-sd-adapt.h"
 
 #include "lldp.h"
 
@@ -394,6 +395,7 @@ test_recv (TestRecvFixture *fixture, gconstpointer user_data)
        gsize i_frames;
        gulong notify_id;
        GError *error = NULL;
+       guint sd_id;
 
        listener = nm_lldp_listener_new ();
        g_assert (listener != NULL);
@@ -403,6 +405,7 @@ test_recv (TestRecvFixture *fixture, gconstpointer user_data)
        notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
                                      (GCallback) lldp_neighbors_changed, &info);
        loop = g_main_loop_new (NULL, FALSE);
+       sd_id = nm_sd_event_attach_default ();
 
        for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
                const TestRecvFrame *f = data->frames[i_frames];
@@ -419,6 +422,7 @@ test_recv (TestRecvFixture *fixture, gconstpointer user_data)
 
        data->check (loop, listener);
 
+       nm_clear_g_source (&sd_id);
        g_clear_pointer (&loop, g_main_loop_unref);
 }
 
index 0f074b0..9bbc299 100644 (file)
@@ -50,6 +50,7 @@
 #include "nm-auth-manager.h"
 #include "nm-core-internal.h"
 #include "nm-exported-object.h"
+#include "nm-sd-adapt.h"
 
 #if !defined(NM_DIST_VERSION)
 # define NM_DIST_VERSION VERSION
@@ -271,6 +272,7 @@ main (int argc, char *argv[])
        gboolean wrote_pidfile = FALSE;
        char *bad_domains = NULL;
        NMConfigCmdLineOptions *config_cli;
+       guint sd_id = 0;
 
        nm_g_type_init ();
 
@@ -474,8 +476,11 @@ main (int argc, char *argv[])
 
        success = TRUE;
 
-       if (configure_and_quit == FALSE)
+       if (configure_and_quit == FALSE) {
+               sd_id = nm_sd_event_attach_default ();
+
                g_main_loop_run (main_loop);
+       }
 
 done:
        nm_exported_object_class_set_quitting ();
@@ -486,5 +491,8 @@ done:
                unlink (global_opt.pidfile);
 
        nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
+
+       nm_clear_g_source (&sd_id);
+
        exit (success ? 0 : 1);
 }
index 225464c..247b771 100644 (file)
@@ -44,6 +44,7 @@ extern unsigned int if_nametoindex (const char *__ifname);
 #include "nm-lndp-rdisc.h"
 #include "nm-utils.h"
 #include "nm-setting-ip6-config.h"
+#include "nm-sd-adapt.h"
 
 #if !defined(NM_DIST_VERSION)
 # define NM_DIST_VERSION VERSION
@@ -345,6 +346,7 @@ main (int argc, char *argv[])
        size_t hwaddr_len = 0;
        gconstpointer tmp;
        gs_free NMUtilsIPv6IfaceId *iid = NULL;
+       guint sd_id;
 
        nm_g_type_init ();
 
@@ -494,6 +496,8 @@ main (int argc, char *argv[])
                nm_rdisc_start (rdisc);
        }
 
+       sd_id = nm_sd_event_attach_default ();
+
        g_main_loop_run (main_loop);
 
        g_clear_pointer (&hwaddr, g_byte_array_unref);
@@ -502,6 +506,8 @@ main (int argc, char *argv[])
                unlink (pidfile);
 
        nm_log_info (LOGD_CORE, "exiting");
+
+       nm_clear_g_source (&sd_id);
        exit (0);
 }
 
index 40d52e7..ec276dc 100644 (file)
 
 #include "nm-sd-adapt.h"
 
-#include <unistd.h>
-#include <errno.h>
-
 #include "sd-event.h"
 #include "fd-util.h"
-#include "time-util.h"
-
-struct sd_event_source {
-       guint refcount;
-       guint id;
-       gpointer user_data;
-
-       GIOChannel *channel;
-
-       union {
-               struct {
-                       sd_event_io_handler_t cb;
-               } io;
-               struct {
-                       sd_event_time_handler_t cb;
-                       uint64_t usec;
-               } time;
-       };
-};
-
-static struct sd_event_source *
-source_new (void)
-{
-       struct sd_event_source *source;
 
-       source = g_slice_new0 (struct sd_event_source);
-       source->refcount = 1;
-       return source;
-}
+/*****************************************************************************/
 
 int
-sd_event_source_set_priority (sd_event_source *s, int64_t priority)
-{
-       return 0;
+asynchronous_close (int fd) {
+       safe_close (fd);
+       return -1;
 }
 
-int
-sd_event_source_set_enabled (sd_event_source *s, int m)
-{
-       /* TODO */
-       g_return_val_if_reached (-EINVAL);
-}
+/*****************************************************************************
+ * Integrating sd_event into glib. Taken and adjusted from
+ * https://www.freedesktop.org/software/systemd/man/sd_event_get_fd.html
+ *****************************************************************************/
 
-int
-sd_event_source_set_time (sd_event_source *s, uint64_t usec)
-{
-       /* TODO */
-       g_return_val_if_reached (-EINVAL);
-}
+typedef struct SDEventSource {
+       GSource source;
+       GPollFD pollfd;
+       sd_event *event;
+       guint *default_source_id;
+} SDEventSource;
 
-sd_event_source*
-sd_event_source_unref (sd_event_source *s)
+static gboolean
+event_prepare (GSource *source, gint *timeout_)
 {
-
-       if (!s)
-               return NULL;
-
-       g_return_val_if_fail (s->refcount, NULL);
-
-       s->refcount--;
-       if (s->refcount == 0) {
-               if (s->id)
-                       g_source_remove (s->id);
-               if (s->channel) {
-                       /* Don't shut down the channel since systemd will soon close
-                        * the file descriptor itself, which would cause -EBADF.
-                        */
-                       g_io_channel_unref (s->channel);
-               }
-               g_slice_free (struct sd_event_source, s);
-       }
-       return NULL;
+       return sd_event_prepare (((SDEventSource *) source)->event) > 0;
 }
 
-int
-sd_event_source_set_description(sd_event_source *s, const char *description)
+static gboolean
+event_check (GSource *source)
 {
-       if (!s)
-               return -EINVAL;
-
-       g_source_set_name_by_id (s->id, description);
-       return 0;
+       return sd_event_wait (((SDEventSource *) source)->event, 0) > 0;
 }
 
 static gboolean
-io_ready (GIOChannel *channel, GIOCondition condition, struct sd_event_source *source)
+event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
 {
-       int r, revents = 0;
-       gboolean result;
-
-       if (condition & G_IO_IN)
-               revents |= EPOLLIN;
-       if (condition & G_IO_OUT)
-               revents |= EPOLLOUT;
-       if (condition & G_IO_PRI)
-               revents |= EPOLLPRI;
-       if (condition & G_IO_ERR)
-               revents |= EPOLLERR;
-       if (condition & G_IO_HUP)
-               revents |= EPOLLHUP;
-
-       source->refcount++;
-
-       r = source->io.cb (source, g_io_channel_unix_get_fd (channel), revents, source->user_data);
-       if (r < 0 || source->refcount <= 1) {
-               source->id = 0;
-               result = G_SOURCE_REMOVE;
-       } else
-               result = G_SOURCE_CONTINUE;
-
-       sd_event_source_unref (source);
-
-       return result;
+       return sd_event_dispatch (((SDEventSource *)source)->event) > 0;
 }
 
-int
-sd_event_add_io (sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata)
+static void
+event_finalize (GSource *source)
 {
-       struct sd_event_source *source;
-       GIOChannel *channel;
-       GIOCondition condition = 0;
-
-       /* systemd supports floating sd_event_source by omitting the @s argument.
-        * We don't have such users and don't implement floating references. */
-       g_return_val_if_fail (s, -EINVAL);
-
-       channel = g_io_channel_unix_new (fd);
-       if (!channel)
-               return -EINVAL;
-
-       source = source_new ();
-       source->io.cb = callback;
-       source->user_data = userdata;
-       source->channel = channel;
-
-       if (events & EPOLLIN)
-               condition |= G_IO_IN;
-       if (events & EPOLLOUT)
-               condition |= G_IO_OUT;
-       if (events & EPOLLPRI)
-               condition |= G_IO_PRI;
-       if (events & EPOLLERR)
-               condition |= G_IO_ERR;
-       if (events & EPOLLHUP)
-               condition |= G_IO_HUP;
-
-       g_io_channel_set_encoding (source->channel, NULL, NULL);
-       g_io_channel_set_buffered (source->channel, FALSE);
-       source->id = g_io_add_watch (source->channel, condition, (GIOFunc) io_ready, source);
-
-       *s = source;
-       return 0;
+       SDEventSource *s;
+
+       s = (SDEventSource *) source;
+       sd_event_unref (s->event);
+       if (s->default_source_id)
+               *s->default_source_id = 0;
 }
 
-static gboolean
-time_ready (struct sd_event_source *source)
+static SDEventSource *
+event_create_source (sd_event *event, guint *default_source_id)
 {
-       source->refcount++;
+       static GSourceFuncs event_funcs = {
+               .prepare = event_prepare,
+               .check = event_check,
+               .dispatch = event_dispatch,
+               .finalize = event_finalize,
+       };
+       SDEventSource *source;
+
+       g_return_val_if_fail (event, NULL);
 
-       source->time.cb (source, source->time.usec, source->user_data);
-       source->id = 0;
+       source = (SDEventSource *) g_source_new (&event_funcs, sizeof (SDEventSource));
 
-       sd_event_source_unref (source);
+       source->event = sd_event_ref (event);
+       source->pollfd.fd = sd_event_get_fd (event);
+       source->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+       source->default_source_id = default_source_id;
 
-       return G_SOURCE_REMOVE;
+       g_source_add_poll ((GSource *) source, &source->pollfd);
+
+       return source;
 }
 
-int
-sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata)
+static guint
+event_attach (sd_event *event, GMainContext *context)
 {
-       struct sd_event_source *source;
-       uint64_t n = now (clock);
+       SDEventSource *source;
+       guint id;
+       int r;
+       sd_event *e = event;
+       guint *p_default_source_id = NULL;
 
-       /* systemd supports floating sd_event_source by omitting the @s argument.
-        * We don't have such users and don't implement floating references. */
-       g_return_val_if_fail (s, -EINVAL);
+       if (!e) {
+               static guint default_source_id = 0;
 
-       source = source_new ();
-       source->time.cb = callback;
-       source->user_data = userdata;
-       source->time.usec = usec;
+               if (default_source_id) {
+                       /* The default event cannot be registered multiple times. */
+                       g_return_val_if_reached (0);
+               }
 
-       if (usec > 1000)
-               usec = n < usec - 1000 ? usec - n : 1000;
-       source->id = g_timeout_add (usec / 1000, (GSourceFunc) time_ready, source);
+               r = sd_event_default (&e);
+               if (r < 0)
+                       g_return_val_if_reached (0);
 
-       *s = source;
-       return 0;
-}
+               p_default_source_id = &default_source_id;
+       }
 
-/* sd_event is basically a GMainContext; but since we only
- * ever use the default context, nothing to do here.
- */
+       source = event_create_source (e, p_default_source_id);
+       id = g_source_attach ((GSource *) source, context);
+       g_source_unref ((GSource *) source);
 
-int
-sd_event_default (sd_event **e)
-{
-       *e = GUINT_TO_POINTER (1);
-       return 0;
-}
 
-sd_event*
-sd_event_ref (sd_event *e)
-{
-       return e;
-}
+       if (!event) {
+               *p_default_source_id = id;
+               sd_event_unref (e);
+       }
 
-sd_event*
-sd_event_unref (sd_event *e)
-{
-       return NULL;
+       g_return_val_if_fail (id, 0);
+       return id;
 }
 
-int
-sd_event_now (sd_event *e, clockid_t clock, uint64_t *usec)
+guint
+nm_sd_event_attach_default (void)
 {
-       *usec = now (clock);
-       return 0;
+       return event_attach (NULL, NULL);
 }
 
-int asynchronous_close(int fd) {
-       safe_close(fd);
-       return -1;
-}
+/*****************************************************************************/
 
index 1547f12..1439aac 100644 (file)
@@ -26,6 +26,8 @@
 #include <sys/resource.h>
 #include <time.h>
 
+guint nm_sd_event_attach_default (void);
+
 #define noreturn G_GNUC_NORETURN
 
 #ifndef CLOCK_BOOTTIME
@@ -115,6 +117,14 @@ G_STMT_START { \
 #define ETHERTYPE_LLDP 0x88cc
 #endif
 
+#ifndef HAVE_SECURE_GETENV
+#  ifdef HAVE___SECURE_GETENV
+#    define secure_getenv __secure_getenv
+#  else
+#    error neither secure_getenv nor __secure_getenv is available
+#  endif
+#endif
+
 /*****************************************************************************/
 
 /* work around missing uchar.h */
@@ -123,6 +133,14 @@ typedef guint32 char32_t;
 
 /*****************************************************************************/
 
+#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+
+static inline int
+sd_notify (int unset_environment, const char *state)
+{
+       return 0;
+}
+
 /* Can't include both net/if.h and linux/if.h; so have to define this here */
 #ifndef IFNAMSIZ
 #define IFNAMSIZ 16
index 841358e..31e8e7f 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include "nm-sd-adapt.h"
+
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <sys/wait.h>
 
+#if 0 /* NM_IGNORED */
 #include "sd-daemon.h"
+#endif
 #include "sd-event.h"
 #include "sd-id128.h"
 
 #include "hashmap.h"
 #include "list.h"
 #include "macro.h"
+#if 0 /* NM_IGNORED */
 #include "missing.h"
+#endif
 #include "prioq.h"
+#if 0 /* NM_IGNORED */
 #include "process-util.h"
+#endif
 #include "set.h"
+#if 0 /* NM_IGNORED */
 #include "signal-util.h"
+#endif
 #include "string-table.h"
 #include "string-util.h"
 #include "time-util.h"
@@ -1126,6 +1136,7 @@ fail:
         return r;
 }
 
+#if 0 /* NM_IGNORED */
 static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
         assert(s);
 
@@ -1192,6 +1203,7 @@ _public_ int sd_event_add_signal(
 
         return 0;
 }
+#endif /* NM_IGNORED */
 
 _public_ int sd_event_add_child(
                 sd_event *e,
@@ -2818,6 +2830,7 @@ _public_ int sd_event_default(sd_event **ret) {
         return 1;
 }
 
+#if 0 /* NM_IGNORED */
 _public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
         assert_return(e, -EINVAL);
         assert_return(tid, -EINVAL);
@@ -2889,3 +2902,4 @@ _public_ int sd_event_get_watchdog(sd_event *e) {
 
         return e->watchdog;
 }
+#endif /* NM_IGNORED */
index ea09d19..01b805a 100644 (file)
@@ -155,7 +155,6 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_boot_id;
         static thread_local bool saved_boot_id_valid = false;
@@ -211,6 +210,7 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         return 0;
 }
 
+#if 0 /* NM_IGNORED */
 _public_ int sd_id128_randomize(sd_id128_t *ret) {
         sd_id128_t t;
         int r;
index a3447fb..597f477 100644 (file)
@@ -117,6 +117,7 @@ test_wired_defname_LDADD = \
 test_systemd_CFLAGS = \
        "-I$(srcdir)/../" \
        "-I$(srcdir)/../platform" \
+       "-I$(srcdir)/../systemd" \
        "-I$(srcdir)/../systemd/src/systemd"
 
 test_systemd_SOURCES = \
index 2f6c479..ceede91 100644 (file)
@@ -21,6 +21,8 @@
 
 #include "sd-dhcp-client.h"
 #include "sd-lldp.h"
+#include "sd-event.h"
+#include "nm-sd-adapt.h"
 
 #include "nm-test-utils.h"
 
@@ -57,6 +59,71 @@ test_lldp_create (void)
 
 /*****************************************************************************/
 
+typedef struct {
+       GMainLoop *mainloop;
+       sd_event_source *event_source;
+} TestSdEventData;
+
+static int
+_test_sd_event_timeout_cb (sd_event_source *s, uint64_t usec, void *userdata)
+{
+       TestSdEventData *user_data = userdata;
+
+       g_assert (user_data);
+       g_assert (user_data->mainloop);
+       g_assert (user_data->event_source);
+
+       user_data->event_source = sd_event_source_unref (user_data->event_source);
+       g_main_loop_quit (user_data->mainloop);
+       return 0;
+}
+
+static void
+test_sd_event (void)
+{
+       int repeat;
+
+       for (repeat = 0; repeat < 2; repeat++) {
+               guint sd_id = 0;
+               int r;
+               int i, n;
+               sd_event *other_events[3] = { NULL }, *event = NULL;
+               TestSdEventData user_data = { 0 };
+
+               g_assert_cmpint (sd_event_default (NULL), ==, 0);
+
+               for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++) {
+                       r = sd_event_default (&other_events[i]);
+                       g_assert (r >= 0 && other_events[i]);
+               }
+
+               sd_id = nm_sd_event_attach_default ();
+
+               r = sd_event_default (&event);
+               g_assert (r >= 0 && event);
+
+               r = sd_event_add_time (event, &user_data.event_source, CLOCK_MONOTONIC, 1, 0, _test_sd_event_timeout_cb, &user_data);
+               g_assert (r >= 0 && user_data.event_source);
+
+               user_data.mainloop = g_main_loop_new (NULL, FALSE);
+               g_main_loop_run (user_data.mainloop);
+               g_main_loop_unref (user_data.mainloop);
+
+               g_assert (!user_data.event_source);
+
+               event = sd_event_unref (event);
+               for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++)
+                       other_events[i] = sd_event_unref (other_events[i]);
+               nm_clear_g_source (&sd_id);
+               for (i = 0, n = G_N_ELEMENTS (other_events); i < n; i++)
+                       other_events[i] = sd_event_unref (other_events[i]);
+
+               g_assert_cmpint (sd_event_default (NULL), ==, 0);
+       }
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -66,6 +133,7 @@ main (int argc, char **argv)
 
        g_test_add_func ("/systemd/dhcp/create", test_dhcp_create);
        g_test_add_func ("/systemd/lldp/create", test_lldp_create);
+       g_test_add_func ("/systemd/sd-event", test_sd_event);
 
        return g_test_run ();
 }