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 \
--- /dev/null
+/*#############################################################################
+# #
+# 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 <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+
+// systemd
+#include <systemd/sd-event.h>
+
+#include <pakfire/client.h>
+#include <pakfire/ctx.h>
+#include <pakfire/builder.h>
+#include <pakfire/util.h>
+#include <pakfire/xfer.h>
+
+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;
+}
--- /dev/null
+/*#############################################################################
+# #
+# 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 <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef PAKFIRE_BUILDER_H
+#define PAKFIRE_BUILDER_H
+
+struct pakfire_builder;
+
+#include <pakfire/client.h>
+#include <pakfire/ctx.h>
+
+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 */
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,
#include <json.h>
+#include <pakfire/builder.h>
#include <pakfire/ctx.h>
+#include <pakfire/xfer.h>
int pakfire_client_create(struct pakfire_client** client,
struct pakfire_ctx* ctx, const char* url, const char* principal);
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 {
#include <systemd/sd-event.h>
#include <pakfire/arch.h>
+#include <pakfire/builder.h>
#include <pakfire/cgroup.h>
#include <pakfire/client.h>
#include <pakfire/ctx.h>
// Pakfire Client
struct pakfire_client* client;
+ // Builder
+ struct pakfire_builder* builder;
+
// URL
char url[PATH_MAX];
sd_event_source* connect_timer;
unsigned int reconnect_holdoff;
- // Timer for submitting stats
- sd_event_source* stats_timer;
-
// cgroup
struct pakfire_cgroup* cgroup;
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 = {};
return r;
}
+#endif
/*
* Prevents that the system can be shut down when there are jobs running...
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)));
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...
*/
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.
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;
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);
ERROR(daemon->ctx, "Could not disable the stats timer: %s\n", strerror(-r));
return r;
}
+#endif
return 0;
}
// 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)
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)
if (r < 0)
goto ERROR;
- // Reconnect after one second
- d->reconnect_holdoff = S_TO_US(1);
-
// Return the pointer
*daemon = d;