source/base: Add the prepare callback functionality
diff --git a/src/sdeventplus/internal/sdevent.hpp b/src/sdeventplus/internal/sdevent.hpp
index e6c420c..dfb6285 100644
--- a/src/sdeventplus/internal/sdevent.hpp
+++ b/src/sdeventplus/internal/sdevent.hpp
@@ -32,6 +32,9 @@
     virtual int
         sd_event_source_set_description(sd_event_source* source,
                                         const char* description) const = 0;
+    virtual int
+        sd_event_source_set_prepare(sd_event_source* source,
+                                    sd_event_handler_t callback) const = 0;
     virtual int sd_event_source_get_pending(sd_event_source* source) const = 0;
     virtual int sd_event_source_get_priority(sd_event_source* source,
                                              int64_t* priority) const = 0;
@@ -101,6 +104,11 @@
     {
         return ::sd_event_source_set_description(source, description);
     }
+    int sd_event_source_set_prepare(sd_event_source* source,
+                                    sd_event_handler_t callback) const override
+    {
+        return ::sd_event_source_set_prepare(source, callback);
+    }
     int sd_event_source_get_pending(sd_event_source* source) const override
     {
         return ::sd_event_source_get_pending(source);
diff --git a/src/sdeventplus/source/base.cpp b/src/sdeventplus/source/base.cpp
index d99d562..d361333 100644
--- a/src/sdeventplus/source/base.cpp
+++ b/src/sdeventplus/source/base.cpp
@@ -1,4 +1,5 @@
 #include <cerrno>
+#include <cstdio>
 #include <exception>
 #include <sdeventplus/exception.hpp>
 #include <sdeventplus/internal/sdevent.hpp>
@@ -17,6 +18,30 @@
     set_enabled(SD_EVENT_OFF);
 }
 
+int Base::prepareCallback()
+{
+    try
+    {
+        prepare(*this);
+        return 0;
+    }
+    catch (const std::system_error& e)
+    {
+        fprintf(stderr, "sdeventplus: prepareCallback: %s\n", e.what());
+        return -e.code().value();
+    }
+    catch (const std::exception& e)
+    {
+        fprintf(stderr, "sdeventplus: prepareCallback: %s\n", e.what());
+        return -ENOSYS;
+    }
+    catch (...)
+    {
+        fprintf(stderr, "sdeventplus: prepareCallback: Unknown error\n");
+        return -ENOSYS;
+    }
+}
+
 const char* Base::get_description()
 {
     const char* description;
@@ -38,6 +63,28 @@
     }
 }
 
+static int prepare_callback(sd_event_source*, void* userdata)
+{
+    if (userdata == nullptr)
+    {
+        fprintf(stderr, "sdeventplus: prepare_callback: Missing userdata\n");
+        return -EINVAL;
+    }
+    return reinterpret_cast<Base*>(userdata)->prepareCallback();
+}
+
+void Base::set_prepare(Callback&& callback)
+{
+    int r = sdevent->sd_event_source_set_prepare(
+        source.get(), callback ? prepare_callback : nullptr);
+    if (r < 0)
+    {
+        prepare = nullptr;
+        throw SdEventError(-r, "sd_event_source_set_prepare");
+    }
+    prepare = std::move(callback);
+}
+
 int Base::get_pending()
 {
     int r = sdevent->sd_event_source_get_pending(source.get());
diff --git a/src/sdeventplus/source/base.hpp b/src/sdeventplus/source/base.hpp
index 20ab820..748f340 100644
--- a/src/sdeventplus/source/base.hpp
+++ b/src/sdeventplus/source/base.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <cstdint>
+#include <functional>
 #include <sdeventplus/internal/sdevent.hpp>
 #include <sdeventplus/internal/sdref.hpp>
 #include <systemd/sd-bus.h>
@@ -14,6 +15,8 @@
 class Base
 {
   public:
+    using Callback = std::function<void(Base& source)>;
+
     virtual ~Base();
 
     // We don't want to allow any kind of slicing.
@@ -22,8 +25,11 @@
     Base(Base&& source) = delete;
     Base& operator=(Base&& source) = delete;
 
+    int prepareCallback();
+
     const char* get_description();
     void set_description(const char* description);
+    void set_prepare(Callback&& callback);
     int get_pending();
     int64_t get_priority();
     void set_priority(int64_t priority);
@@ -39,6 +45,9 @@
          internal::SdEvent* sdevent = &internal::sdevent_impl);
     Base(sd_event_source* source, std::false_type,
          internal::SdEvent* sdevent = &internal::sdevent_impl);
+
+  private:
+    Callback prepare;
 };
 
 } // namespace source
diff --git a/test/source/base.cpp b/test/source/base.cpp
index ce95f12..13c534a 100644
--- a/test/source/base.cpp
+++ b/test/source/base.cpp
@@ -1,3 +1,4 @@
+#include <cerrno>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <sdeventplus/exception.hpp>
@@ -5,6 +6,7 @@
 #include <sdeventplus/source/base.hpp>
 #include <sdeventplus/test/sdevent.hpp>
 #include <string>
+#include <system_error>
 #include <type_traits>
 
 namespace sdeventplus
@@ -16,6 +18,7 @@
 
 using testing::DoAll;
 using testing::Return;
+using testing::SaveArg;
 using testing::SetArgPointee;
 
 class BaseImpl : public Base
@@ -132,6 +135,73 @@
     EXPECT_THROW(base->set_description(expected), SdEventError);
 }
 
+TEST_F(BaseMethodTest, SetPrepareCallback)
+{
+    bool completed = false;
+    Base::Callback callback = [&completed](Base&) { completed = true; };
+    sd_event_handler_t event_handler;
+    EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+        .WillOnce(DoAll(SaveArg<1>(&event_handler), Return(0)));
+    base->set_prepare(std::move(callback));
+    EXPECT_FALSE(callback);
+    EXPECT_FALSE(completed);
+
+    EXPECT_EQ(0, event_handler(nullptr, base.get()));
+    EXPECT_TRUE(completed);
+}
+
+TEST_F(BaseMethodTest, SetPrepareCallbackNoUserdata)
+{
+    Base::Callback callback = [](Base&) {};
+    sd_event_handler_t event_handler;
+    EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+        .WillOnce(DoAll(SaveArg<1>(&event_handler), Return(0)));
+    base->set_prepare(std::move(callback));
+    EXPECT_FALSE(callback);
+
+    EXPECT_EQ(-EINVAL, event_handler(nullptr, nullptr));
+}
+
+TEST_F(BaseMethodTest, SetPrepareNull)
+{
+    EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, nullptr))
+        .WillOnce(Return(0));
+    base->set_prepare(nullptr);
+    EXPECT_EQ(-ENOSYS, base->prepareCallback());
+}
+
+TEST_F(BaseMethodTest, SetPrepareSystemError)
+{
+    Base::Callback callback = [](Base&) {
+        throw std::system_error(EBUSY, std::generic_category());
+    };
+    EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+        .WillOnce(Return(0));
+    base->set_prepare(std::move(callback));
+    EXPECT_FALSE(callback);
+    EXPECT_EQ(-EBUSY, base->prepareCallback());
+}
+
+TEST_F(BaseMethodTest, SetPrepareUnknownException)
+{
+    Base::Callback callback = [](Base&) { throw static_cast<int>(1); };
+    EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+        .WillOnce(Return(0));
+    base->set_prepare(std::move(callback));
+    EXPECT_FALSE(callback);
+    EXPECT_EQ(-ENOSYS, base->prepareCallback());
+}
+
+TEST_F(BaseMethodTest, SetPrepareError)
+{
+    Base::Callback callback = [](Base&) {};
+    EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+        .WillOnce(Return(-EINVAL));
+    EXPECT_THROW(base->set_prepare(std::move(callback)), SdEventError);
+    EXPECT_TRUE(callback);
+    EXPECT_EQ(-ENOSYS, base->prepareCallback());
+}
+
 TEST_F(BaseMethodTest, GetPendingSuccess)
 {
     EXPECT_CALL(mock, sd_event_source_get_pending(expected_source))