From: Michael Tremer Date: Wed, 25 Jun 2025 15:48:48 +0000 (+0000) Subject: client: Move builder stuff into a separate object X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=412173d3e7913367105125ec505b7e31eb74ef30;p=pakfire.git client: Move builder stuff into a separate object This is messy as hell. I moved so much stuff around and lost track of most of it. So this is a commit to kind of have some baseline. Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 6d832810..ceea71da 100644 --- a/Makefile.am +++ b/Makefile.am @@ -198,6 +198,8 @@ libpakfire_la_SOURCES = \ src/pakfire/buffer.h \ src/pakfire/build.c \ src/pakfire/build.h \ + src/pakfire/builder.c \ + src/pakfire/builder.h \ src/pakfire/cgroup.c \ src/pakfire/cgroup.h \ src/pakfire/client.c \ diff --git a/src/pakfire/builder.c b/src/pakfire/builder.c new file mode 100644 index 00000000..431d6534 --- /dev/null +++ b/src/pakfire/builder.c @@ -0,0 +1,167 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2025 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program 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 General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include + +// systemd +#include + +#include +#include +#include +#include +#include + +struct pakfire_builder { + // Context + struct pakfire_ctx* ctx; + + // Reference Counter + int nrefs; + + // Event Loop + sd_event* loop; + + // Client + struct pakfire_client* client; + + // Control Connection + struct pakfire_xfer* control; + + // Reconnect Timer & Holdoff Time + sd_event_source* reconnect_timer; + uint64_t reconnect_holdoff; +}; + +static void pakfire_builder_free(struct pakfire_builder* self) { + if (self->client) + pakfire_client_unref(self->client); + if (self->control) + pakfire_xfer_unref(self->control); + if (self->ctx) + pakfire_ctx_unref(self->ctx); + free(self); +} + +int pakfire_builder_create(struct pakfire_builder** builder, + struct pakfire_ctx* ctx, struct pakfire_client* client) { + struct pakfire_builder* self = NULL; + int r; + + // Allocate some memory + self = calloc(1, sizeof(*self)); + if (!self) + return -errno; + + // Store a reference to the context + self->ctx = pakfire_ctx_ref(ctx); + + // Initialize the reference counter + self->nrefs = 1; + + // Fetch a reference to the event loop + r = pakfire_ctx_loop(self->ctx, &self->loop); + if (r < 0) + goto ERROR; + + // Store a reference to the client + self->client = pakfire_client_ref(client); + + // Return the pointer + *builder = self; + return 0; + +ERROR: + pakfire_builder_free(self); + return r; +} + +struct pakfire_builder* pakfire_builder_ref(struct pakfire_builder* self) { + ++self->nrefs; + + return self; +} + +struct pakfire_builder* pakfire_builder_unref(struct pakfire_builder* self) { + if (--self->nrefs > 0) + return self; + + pakfire_builder_free(self); + return NULL; +} + +int pakfire_builder_connect(struct pakfire_builder* self) { + // Bail if we are already connected + if (self->control) + return -EBUSY; + + return pakfire_client_builder_connect(self->client, self); +} + +int pakfire_builder_connected(struct pakfire_xfer* xfer, void* data) { + struct pakfire_builder* self = data; + + DEBUG(self->ctx, "Builder connected\n"); + + // Store a reference to the control connection + self->control = pakfire_xfer_ref(xfer); + + return 0; +} + +/* + This function is called whenever the connection to the build service could not + be established or was interrupted. It will try to reconnect. +*/ +int pakfire_builder_close(struct pakfire_xfer* xfer, int code, void* data) { + struct pakfire_builder* self = data; + int r; + + // Remove the connection from the client + r = pakfire_client_builder_disconnected(self->client, xfer); + if (r < 0) + return r; + + // Drop the control connection + if (self->control) { + pakfire_xfer_unref(self->control); + self->control = NULL; + } + + INFO(self->ctx, "Will attempt to reconnect in %lu second(s)\n", + US_TO_S(self->reconnect_holdoff)); + + // Set the reconnection timer + r = sd_event_source_set_time_relative( + self->reconnect_timer, self->reconnect_holdoff); + if (r < 0) { + ERROR(self->ctx, "Failed to set the reconnection timer: %s\n", strerror(-r)); + return r; + } + + // Double the holdoff time + self->reconnect_holdoff *= 2; + + // Cap reconnection attempts to every minute + if (self->reconnect_holdoff > S_TO_US(60)) + self->reconnect_holdoff = S_TO_US(60); + + return 0; +} diff --git a/src/pakfire/builder.h b/src/pakfire/builder.h new file mode 100644 index 00000000..01b14bc7 --- /dev/null +++ b/src/pakfire/builder.h @@ -0,0 +1,42 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2025 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program 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 General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#ifndef PAKFIRE_BUILDER_H +#define PAKFIRE_BUILDER_H + +struct pakfire_builder; + +#include +#include + +int pakfire_builder_create(struct pakfire_builder** builder, + struct pakfire_ctx* ctx, struct pakfire_client* client); + +struct pakfire_builder* pakfire_builder_ref(struct pakfire_builder* self); +struct pakfire_builder* pakfire_builder_unref(struct pakfire_builder* self); + +// Connect +int pakfire_builder_connect(struct pakfire_builder* self); + +// Socket Callbacks +int pakfire_builder_connected(struct pakfire_xfer* xfer, void* data); +int pakfire_builder_close(struct pakfire_xfer* xfer, int code, void* data); + +#endif /* PAKFIRE_BUILDER_H */ diff --git a/src/pakfire/client.c b/src/pakfire/client.c index 2d79ace4..dc293dbd 100644 --- a/src/pakfire/client.c +++ b/src/pakfire/client.c @@ -705,6 +705,145 @@ ERROR: return r; } +#if 0 +int pakfire_client_auth_builder(struct pakfire_client* self, + const char* username, const char* password) { + struct pakfire_xfer* xfer = NULL; + char hostname[HOST_NAME_MAX]; + struct { + char* username; + char* password; + char* hostname; + } credentials = {}; + char payload[2048]; + int r; + + // Fetch the hostname + r = gethostname(hostname, sizeof(hostname)); + if (r) + goto ERROR; + + // Create a new xfer + r = pakfire_client_xfer_create(&xfer, self, "/api/v1/auth/builder"); + if (r < 0) + goto ERROR; + + // Set the header + r = pakfire_xfer_add_header(xfer, "Content-Type: application/x-www-form-urlencoded"); + if (r < 0) + goto ERROR; + + // Escape the credentials + credentials.username = pakfire_xfer_escape(xfer, username); + credentials.password = pakfire_xfer_escape(xfer, password); + + // Escape the hostname + credentials.hostname = pakfire_xfer_escape(xfer, hostname); + + // Format the payload + r = pakfire_string_format(payload, "username=%s&password=%s&hostname=%s", + credentials.username, credentials.password, credentials.hostname); + if (r < 0) + goto ERROR; + + // Set the payload + r = pakfire_xfer_set_payload(xfer, payload); + if (r < 0) + goto ERROR; + + // Register the callback + r = pakfire_xfer_set_response_callback(xfer, pakfire_client_auth_response, self); + if (r < 0) + goto ERROR; + + // Enqueue the transfer + r = pakfire_httpclient_enqueue(self->httpclient, xfer); + if (r < 0) + goto ERROR; + +ERROR: + if (xfer) + pakfire_xfer_unref(xfer); + if (credentials.username) + free(credentials.username); + if (credentials.password) + free(credentials.password); + if (credentials.hostname) + free(credentials.hostname); + + return r; +} +#endif + +// Builder + +int pakfire_client_builder(struct pakfire_builder** builder, struct pakfire_client* self) { + struct pakfire_builder* b = NULL; + int r; + + // Create a new builder + r = pakfire_builder_create(&b, self->ctx, self); + if (r < 0) + goto ERROR; + + // Return the pointer + *builder = b; + return 0; + +ERROR: + if (b) + pakfire_builder_unref(b); + + return r; +} + +int pakfire_client_builder_connect(struct pakfire_client* self, struct pakfire_builder* builder) { + struct pakfire_xfer* xfer = NULL; + int r; + + // Create a new xfer + r = pakfire_client_xfer_create(&xfer, self, "/api/v1/builders/control"); + if (r < 0) + goto ERROR; + + // Enable authentication + r = pakfire_client_xfer_auth(self, xfer); + if (r < 0) + goto ERROR; + + // Make this a WebSocket connection + r = pakfire_xfer_socket(xfer, + pakfire_builder_connected, + NULL, + NULL, + pakfire_builder_close, + builder); + if (r < 0) + goto ERROR; + + // Enqueue the transfer + r = pakfire_httpclient_enqueue(self->httpclient, xfer); + if (r < 0) + goto ERROR; + +ERROR: + if (xfer) + pakfire_xfer_unref(xfer); + + return r; +} + +int pakfire_client_builder_disconnected(struct pakfire_client* self, struct pakfire_xfer* xfer) { + int r; + + // Remove the connection from the client + r = pakfire_httpclient_dequeue(self->httpclient, xfer); + if (r < 0) + ERROR(self->ctx, "Failed to remove the control connection: %s\n", strerror(-r)); + + return r; +} + // Build int pakfire_client_build(struct pakfire_client* self, const char* upload, diff --git a/src/pakfire/client.h b/src/pakfire/client.h index 0fc9b16d..8237ae4a 100644 --- a/src/pakfire/client.h +++ b/src/pakfire/client.h @@ -25,7 +25,9 @@ struct pakfire_client; #include +#include #include +#include int pakfire_client_create(struct pakfire_client** client, struct pakfire_ctx* ctx, const char* url, const char* principal); @@ -59,6 +61,14 @@ int pakfire_client_set_auth_callback(struct pakfire_client* client, int pakfire_client_auth_user(struct pakfire_client* client, const char* password); +// Builder + +int pakfire_client_builder(struct pakfire_builder** builder, struct pakfire_client* self); + +// Low-level functions +int pakfire_client_builder_connect(struct pakfire_client* self, struct pakfire_builder* builder); +int pakfire_client_builder_disconnected(struct pakfire_client* self, struct pakfire_xfer* xfer); + // Builds typedef enum pakfire_client_build_flags { diff --git a/src/pakfire/daemon.c b/src/pakfire/daemon.c index 178c1932..3ba57a6e 100644 --- a/src/pakfire/daemon.c +++ b/src/pakfire/daemon.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -55,6 +56,9 @@ struct pakfire_daemon { // Pakfire Client struct pakfire_client* client; + // Builder + struct pakfire_builder* builder; + // URL char url[PATH_MAX]; @@ -72,9 +76,6 @@ struct pakfire_daemon { sd_event_source* connect_timer; unsigned int reconnect_holdoff; - // Timer for submitting stats - sd_event_source* stats_timer; - // cgroup struct pakfire_cgroup* cgroup; @@ -130,6 +131,7 @@ static int pakfire_daemon_terminate(sd_event_source* source, return sd_event_exit(sd_event_source_get_event(source), 0); } +#if 0 static int pakfire_daemon_submit_stats(sd_event_source* s, uint64_t usec, void* p) { struct pakfire_daemon* daemon = p; struct pakfire_cpustat cpustat = {}; @@ -352,6 +354,7 @@ ERROR: return r; } +#endif /* * Prevents that the system can be shut down when there are jobs running... @@ -634,61 +637,6 @@ static int pakfire_daemon_send(struct pakfire_xfer* xfer, void* data) { return 0; } -/* - 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; - - // Remove the connection from the client - r = pakfire_httpclient_dequeue(daemon->httpclient, xfer); - if (r < 0) { - ERROR(daemon->ctx, "Failed to remove the control connection: %s\n", strerror(-r)); - return r; - } - - // Drop the control connection - if (daemon->control) { - pakfire_xfer_unref(daemon->control); - daemon->control = NULL; - } - - INFO(daemon->ctx, "Will attempt to reconnect in %lu second(s)\n", - daemon->reconnect_holdoff / S_TO_US(1)); - - // Set the reconnection timer - r = sd_event_source_set_time_relative(daemon->connect_timer, daemon->reconnect_holdoff); - if (r < 0) { - 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) { - 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 > S_TO_US(60)) - daemon->reconnect_holdoff = S_TO_US(60); - - // Disable sending stats until we are connected - r = sd_event_source_set_enabled(daemon->stats_timer, SD_EVENT_OFF); - if (r < 0) { - ERROR(daemon->ctx, "Could not disable the stats timer: %s\n", strerror(-r)); - return r; - } - - return 0; -} - static int pakfire_daemon_xfer_create(struct pakfire_xfer** xfer, struct pakfire_daemon* daemon, const char* url, ...) __attribute__((format(printf, 3, 4))); @@ -721,158 +669,6 @@ ERROR: return r; } -static int pakfire_daemon_connected(struct pakfire_xfer* xfer, void* data) { - struct pakfire_daemon* daemon = data; - int r; - - DEBUG(daemon->ctx, "Connected!\n"); - - // Update the process title - r = pakfire_proctitle_set("pakfire-daemon: Connected\n"); - if (r < 0) - return r; - - // Store a reference to the control connection - daemon->control = pakfire_xfer_ref(xfer); - - // Reset the holdoff timer - daemon->reconnect_holdoff = S_TO_US(1); - - // Submit stats continuously - r = sd_event_source_set_enabled(daemon->stats_timer, SD_EVENT_ON); - if (r < 0) { - ERROR(daemon->ctx, "Could not activate the stat timer: %s\n", strerror(-r)); - return r; - } - - return 0; -} - -static int pakfire_daemon_connect(struct pakfire_daemon* daemon) { - struct pakfire_sysinfo sysinfo = {}; - struct pakfire_cpuinfo cpuinfo = {}; - struct pakfire_xfer* xfer = NULL; - const char* arch = NULL; - const char* id = NULL; - int r; - - DEBUG(daemon->ctx, "Connecting...\n"); - - // Update the process title - r = pakfire_proctitle_set("pakfire-daemon: Connecting...\n"); - if (r < 0) - return r; - - // Fetch the distro - const struct pakfire_distro* distro = pakfire_ctx_get_distro(daemon->ctx); - - // Fetch system information - r = pakfire_sysinfo(&sysinfo); - if (r < 0) { - ERROR(daemon->ctx, "Failed to fetch system info: %s\n", strerror(-r)); - goto ERROR; - } - - // Fetch native arch - arch = pakfire_arch_native(); - if (!arch) { - ERROR(daemon->ctx, "Failed to fetch native arch: %s\n", strerror(errno)); - r = -errno; - goto ERROR; - } - - // Fetch CPU info - r = pakfire_cpuinfo(&cpuinfo); - if (r) { - ERROR(daemon->ctx, "Failed to fetch CPU info: %s\n", strerror(-r)); - goto ERROR; - } - - // Create a new xfer - r = pakfire_daemon_xfer_create(&xfer, daemon, "/api/v1/builders/control"); - if (r) - goto ERROR; - - // Enable authentication - r = pakfire_xfer_auth(xfer); - if (r) - goto ERROR; - - // Send our version - r = pakfire_xfer_add_query(xfer, "version", "%s", PACKAGE_FULLVERSION); - if (r < 0) - goto ERROR; - - // System Vendor - if (*sysinfo.vendor) { - r = pakfire_xfer_add_query(xfer, "sys_vendor", "%s", sysinfo.vendor); - if (r < 0) - goto ERROR; - } - - // System Model - if (*sysinfo.name) { - r = pakfire_xfer_add_query(xfer, "sys_name", "%s", sysinfo.name); - if (r < 0) - goto ERROR; - } - - // CPU Model - if (*cpuinfo.model) { - r = pakfire_xfer_add_query(xfer, "cpu_model", "%s", cpuinfo.model); - if (r < 0) - goto ERROR; - } - - // CPU Count - r = pakfire_xfer_add_query(xfer, "cpu_count", "%u", cpuinfo.count); - if (r < 0) - goto ERROR; - - // Arch - r = pakfire_xfer_add_query(xfer, "arch", "%s", arch); - if (r < 0) - goto ERROR; - - // OS - r = pakfire_xfer_add_query(xfer, "os_name", "%s", distro->pretty_name); - if (r < 0) - goto ERROR; - - // Add all running jobs - for (unsigned int i = 0; i < MAX_JOBS; i++) { - if (!daemon->jobs[i]) - break; - - // Fetch the job ID - id = pakfire_job_get_id(daemon->jobs[i]); - if (!id) - continue; - - // Add the job ID to the query - r = pakfire_xfer_add_query(xfer, "jobs", "%s", id); - if (r < 0) - goto ERROR; - } - - // Make this a WebSocket connection - r = pakfire_xfer_socket(xfer, pakfire_daemon_connected, - pakfire_daemon_recv, pakfire_daemon_send, pakfire_daemon_close, daemon); - if (r) - goto ERROR; - - // Enqueue the transfer - r = pakfire_httpclient_enqueue(daemon->httpclient, xfer); - if (r) - goto ERROR; - -ERROR: - if (xfer) - pakfire_xfer_unref(xfer); - - return r; -} - /* Called when the client is ready and we can start making connections... */ @@ -880,7 +676,7 @@ static int pakfire_daemon_ready(struct pakfire_client* client, void* data) { struct pakfire_daemon* self = data; // Connect the control connection - return pakfire_daemon_connect(self); + return pakfire_builder_connect(self->builder); } // Currently we are not doing anything here. We just need to block SIGCHLD. @@ -959,8 +755,8 @@ static int pakfire_daemon_setup_bus(struct pakfire_daemon* daemon) { static int pakfire_daemon_setup_loop(struct pakfire_daemon* daemon) { int r; - // Fetch a reference to the default event loop - r = sd_event_default(&daemon->loop); + // Fetch a reference to the context's event loop + r = pakfire_ctx_loop(daemon->ctx, &daemon->loop); if (r < 0) { ERROR(daemon->ctx, "Could not setup event loop: %s\n", strerror(-r)); return r; @@ -1005,7 +801,6 @@ static int pakfire_daemon_setup_loop(struct pakfire_daemon* daemon) { ERROR(daemon->ctx, "Could not register the connection timer: %s\n", strerror(-r)); return r; } -#endif // Disable the reconnection timer until we are authenticated r = sd_event_source_set_enabled(daemon->connect_timer, SD_EVENT_OFF); @@ -1028,6 +823,7 @@ static int pakfire_daemon_setup_loop(struct pakfire_daemon* daemon) { ERROR(daemon->ctx, "Could not disable the stats timer: %s\n", strerror(-r)); return r; } +#endif return 0; } @@ -1064,10 +860,10 @@ static void pakfire_daemon_free(struct pakfire_daemon* daemon) { // Release shutdown inhibition pakfire_daemon_release_inhibit_shutdown(daemon); + if (daemon->builder) + pakfire_builder_unref(daemon->builder); if (daemon->connect_timer) sd_event_source_unref(daemon->connect_timer); - if (daemon->stats_timer) - sd_event_source_unref(daemon->stats_timer); if (daemon->httpclient) pakfire_httpclient_unref(daemon->httpclient); if (daemon->client) @@ -1119,13 +915,18 @@ int pakfire_daemon_create(struct pakfire_daemon** daemon, struct pakfire_ctx* ct goto ERROR; // Connect to the build service - r = pakfire_client_create(&d->client, d->ctx, d->loop, d->url, NULL); + r = pakfire_client_create(&d->client, d->ctx, d->url, NULL); if (r < 0) goto ERROR; // Register the ready callback pakfire_client_set_ready_callback(d->client, pakfire_daemon_ready, d); + // Create builder + r = pakfire_client_builder(&d->builder, d->client); + if (r < 0) + goto ERROR; + // Create the cgroup r = pakfire_cgroup_create(&d->cgroup, d->ctx, NULL, "pakfire-daemon", 0); if (r < 0) @@ -1136,9 +937,6 @@ int pakfire_daemon_create(struct pakfire_daemon** daemon, struct pakfire_ctx* ct if (r < 0) goto ERROR; - // Reconnect after one second - d->reconnect_holdoff = S_TO_US(1); - // Return the pointer *daemon = d;