]> git.ipfire.org Git - pakfire.git/commitdiff
daemon: Automatically try to reconnect when the connection gets lost
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 13 Aug 2024 18:04:29 +0000 (18:04 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 13 Aug 2024 18:04:29 +0000 (18:04 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/daemon.c

index bb5c5d306ef5864d6e8178c08333002abb7c17f5..3c37c87b88b10b0c3031d068cdaa613b451e303b 100644 (file)
@@ -38,6 +38,10 @@ struct pakfire_daemon {
 
        // Event Loop
        sd_event* loop;
+
+       // Connection Timer and Holdoff Time
+       sd_event_source* connect_timer;
+       unsigned int reconnect_holdoff;
 };
 
 static int pakfire_daemon_terminate(sd_event_source* source,
@@ -49,6 +53,108 @@ static int pakfire_daemon_terminate(sd_event_source* source,
        return sd_event_exit(sd_event_source_get_event(source), 0);
 }
 
+/*
+       Called when a new job has been received
+*/
+static int pakfire_daemon_job(struct pakfire_daemon* daemon, json_object* m) {
+       return 0;
+}
+
+static int pakfire_daemon_recv(struct pakfire_xfer* xfer,
+               const char* message, const size_t size, void* data) {
+       struct pakfire_daemon* daemon = data;
+       struct json_object* type = NULL;
+       struct json_object* m = NULL;
+       int r;
+
+       // Reset the holdoff timer
+       daemon->reconnect_holdoff = 1000000;
+
+       // Parse the JSON message
+       m = pakfire_json_parse(daemon->ctx, message, size);
+       if (!m) {
+               r = -errno;
+               goto ERROR;
+       }
+
+       // Fetch the message type
+       if (!json_object_object_get_ex(m, "type", &type)) {
+               CTX_ERROR(daemon->ctx, "Invalid message with missing type\n");
+
+               return -EINVAL;
+       }
+
+       // Fetch the type
+       const char* t = json_object_get_string(type);
+
+       // Handle new jobs
+       if (strcmp(t, "job") == 0) {
+               r = pakfire_daemon_job(daemon, m);
+
+       } else {
+               CTX_ERROR(daemon->ctx, "Unknown message. Ignoring:\n%s\n",
+                       json_object_to_json_string_ext(m, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY));
+
+               r = 0;
+       }
+
+ERROR:
+       if (m)
+               json_object_put(m);
+
+       return r;
+}
+
+/*
+       This function is called whenever the connection to the build service could not
+       be established or was interrupted. It will try to reconnect.
+*/
+static int pakfire_daemon_close(struct pakfire_xfer* xfer, int code, void* data) {
+       struct pakfire_daemon* daemon = data;
+       int r;
+
+       CTX_INFO(daemon->ctx, "Will attempt to reconnect in %u second(s)\n",
+               daemon->reconnect_holdoff / 1000000);
+
+       // Set the reconnection timer
+       r = sd_event_source_set_time_relative(daemon->connect_timer, daemon->reconnect_holdoff);
+       if (r < 0) {
+               CTX_ERROR(daemon->ctx, "Could not set the reconnection timer: %s\n", strerror(-r));
+               return r;
+       }
+
+       // Activate the timer
+       r = sd_event_source_set_enabled(daemon->connect_timer, SD_EVENT_ONESHOT);
+       if (r < 0) {
+               CTX_ERROR(daemon->ctx, "Could not activate the connect timer: %s\n", strerror(-r));
+               return r;
+       }
+
+       // Double the holdoff time
+       daemon->reconnect_holdoff *= 2;
+
+       // Cap reconnection attempts to every minute
+       if (daemon->reconnect_holdoff > 60000000)
+               daemon->reconnect_holdoff = 60000000;
+
+       return 0;
+}
+
+static int pakfire_daemon_connect(sd_event_source* s, uint64_t usec, void* data) {
+       struct pakfire_daemon* daemon = data;
+       int r;
+
+       CTX_INFO(daemon->ctx, "Connecting...\n");
+
+       // Connect to the build service
+       r = pakfire_buildservice_daemon(daemon->service,
+                       pakfire_daemon_recv, NULL, pakfire_daemon_close, daemon);
+       if (r)
+               CTX_ERROR(daemon->ctx, "Connection attempt failed\n");
+
+       return r;
+}
+
 static int pakfire_daemon_setup_loop(struct pakfire_daemon* daemon) {
        int r;
 
@@ -56,14 +162,14 @@ static int pakfire_daemon_setup_loop(struct pakfire_daemon* daemon) {
        r = sd_event_default(&daemon->loop);
        if (r < 0) {
                CTX_ERROR(daemon->ctx, "Could not setup event loop: %s\n", strerror(-r));
-               return 1;
+               return r;
        }
 
        // Enable the watchdog
        r = sd_event_set_watchdog(daemon->loop, 1);
        if (r < 0) {
                CTX_ERROR(daemon->ctx, "Could not activate watchdog: %s\n", strerror(-r));
-               return 1;
+               return r;
        }
 
        // Listen for SIGTERM
@@ -71,7 +177,7 @@ static int pakfire_daemon_setup_loop(struct pakfire_daemon* daemon) {
                pakfire_daemon_terminate, daemon);
        if (r < 0) {
                CTX_ERROR(daemon->ctx, "Could not register handling SIGTERM: %s\n", strerror(-r));
-               return 1;
+               return r;
        }
 
        // Listen for SIGINT
@@ -79,13 +185,23 @@ static int pakfire_daemon_setup_loop(struct pakfire_daemon* daemon) {
                pakfire_daemon_terminate, daemon);
        if (r < 0) {
                CTX_ERROR(daemon->ctx, "Could not register handling SIGINT: %s\n", strerror(-r));
-               return 1;
+               return r;
+       }
+
+       // Setup the reconnection timer
+       r = sd_event_add_time_relative(daemon->loop, &daemon->connect_timer,
+               CLOCK_MONOTONIC, 0, 0, pakfire_daemon_connect, daemon);
+       if (r < 0) {
+               CTX_ERROR(daemon->ctx, "Could not register the connection timer: %s\n", strerror(-r));
+               return r;
        }
 
        return 0;
 }
 
 static void pakfire_daemon_free(struct pakfire_daemon* daemon) {
+       if (daemon->connect_timer)
+               sd_event_source_unref(daemon->connect_timer);
        if (daemon->service)
                pakfire_buildservice_unref(daemon->service);
        if (daemon->loop)
@@ -120,6 +236,9 @@ int pakfire_daemon_create(struct pakfire_daemon** daemon, struct pakfire_ctx* ct
        if (r)
                goto ERROR;
 
+       // Reconnect after one second
+       d->reconnect_holdoff = 1000000;
+
        // Return the pointer
        *daemon = d;
 
@@ -146,55 +265,6 @@ struct pakfire_daemon* pakfire_daemon_unref(struct pakfire_daemon* daemon) {
        return NULL;
 }
 
-/*
-       Called when a new job has been received
-*/
-static int pakfire_daemon_job(struct pakfire_daemon* daemon, json_object* m) {
-       return 0;
-}
-
-static int pakfire_daemon_recv(struct pakfire_xfer* xfer,
-               const char* message, const size_t size, void* data) {
-       struct pakfire_daemon* daemon = data;
-       struct json_object* type = NULL;
-       struct json_object* m = NULL;
-       int r;
-
-       // Parse the JSON message
-       m = pakfire_json_parse(daemon->ctx, message, size);
-       if (!m) {
-               r = -errno;
-               goto ERROR;
-       }
-
-       // Fetch the message type
-       if (!json_object_object_get_ex(m, "type", &type)) {
-               CTX_ERROR(daemon->ctx, "Invalid message with missing type\n");
-
-               return -EINVAL;
-       }
-
-       // Fetch the type
-       const char* t = json_object_get_string(type);
-
-       // Handle new jobs
-       if (strcmp(t, "job") == 0) {
-               r = pakfire_daemon_job(daemon, m);
-
-       } else {
-               CTX_ERROR(daemon->ctx, "Unknown message. Ignoring:\n%s\n",
-                       json_object_to_json_string_ext(m, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY));
-
-               r = 0;
-       }
-
-ERROR:
-       if (m)
-               json_object_put(m);
-
-       return r;
-}
-
 static int pakfire_daemon_loop(struct pakfire_daemon* daemon) {
        int r;
 
@@ -220,13 +290,5 @@ ERROR:
 }
 
 int pakfire_daemon_main(struct pakfire_daemon* daemon) {
-       int r;
-
-       // Connect to the build service
-       r = pakfire_buildservice_daemon(daemon->service,
-               pakfire_daemon_recv, NULL, NULL, daemon);
-       if (r)
-               return r;
-
        return pakfire_daemon_loop(daemon);
 }