first commit
This commit is contained in:
191
src/config.c
Normal file
191
src/config.c
Normal file
@ -0,0 +1,191 @@
|
||||
#include <errno.h>
|
||||
#include <ini.h>
|
||||
#include <libmp/all.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "intl.h"
|
||||
#include "log.h"
|
||||
|
||||
#define option_count() sizeof(config.options) / sizeof(config.options[0])
|
||||
|
||||
config_t config = {
|
||||
.options =
|
||||
{
|
||||
{.key = "threads", .type = TYPE_INTEGER, .def.integer = 10},
|
||||
{.key = "addr", .type = TYPE_STRING, .def.string = "0.0.0.0:5858"},
|
||||
{.key = "dir", .type = TYPE_STRING, .def.string = NULL},
|
||||
{.key = "log", .type = TYPE_STRING, .def.string = NULL},
|
||||
},
|
||||
.pools = NULL,
|
||||
};
|
||||
|
||||
pool_config_t *config_pool_add(char *name) {
|
||||
pool_config_t *pool = malloc(sizeof(pool_config_t));
|
||||
bzero(pool, sizeof(pool_config_t));
|
||||
|
||||
pool->name = strdup(name);
|
||||
pool->next = config.pools;
|
||||
config.pools = pool;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
bool config_pool_contains(char *name) {
|
||||
pool_config_t *cur = config.pools;
|
||||
while (NULL != cur) {
|
||||
if (eq(cur->name, name))
|
||||
return true;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void config_option_set(config_option_t *op, char *value) {
|
||||
switch (op->type) {
|
||||
case TYPE_STRING:
|
||||
free(op->val.string);
|
||||
op->val.string = strdup(value);
|
||||
break;
|
||||
|
||||
case TYPE_INTEGER:
|
||||
op->val.integer = atoi(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void config_option_free(config_option_t *op) {
|
||||
switch (op->type) {
|
||||
case TYPE_STRING:
|
||||
free(op->val.string);
|
||||
break;
|
||||
|
||||
case TYPE_INTEGER:
|
||||
op->val.integer = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_set(char *key, char *value) {
|
||||
for (int i = 0; i < option_count(); i++) {
|
||||
if (eq(config.options[i].key, key)) {
|
||||
config_option_set(&config.options[i], value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void config_options_clear() {
|
||||
for (int i = 0; i < option_count(); i++) {
|
||||
config_option_t *cur = &config.options[i];
|
||||
switch (cur->type) {
|
||||
case TYPE_STRING:
|
||||
cur->val.string = NULL;
|
||||
break;
|
||||
|
||||
case TYPE_INTEGER:
|
||||
cur->val.integer = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int config_load_handler(void *data, const char *_section, const char *_key, const char *_value) {
|
||||
char *section = (char *)_section, *key = (char *)_key, *value = (char *)_value;
|
||||
|
||||
if (eq("", section)) {
|
||||
if (!config_set(key, value))
|
||||
goto unknown;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!config_pool_contains(section))
|
||||
config_pool_add(section);
|
||||
|
||||
if (eq(key, "host")) {
|
||||
free(config.pools->host);
|
||||
config.pools->host = strdup(value);
|
||||
}
|
||||
|
||||
else
|
||||
goto unknown;
|
||||
|
||||
return 1;
|
||||
|
||||
unknown:
|
||||
log_error("Unknown configuration option: %s/%s", section, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool config_load(char *file) {
|
||||
config_options_clear();
|
||||
|
||||
if (!exists(file) || !can_read(file)) {
|
||||
log_error("Failed to access the configuration file: %s", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ini_parse(file, config_load_handler, NULL) < 0) {
|
||||
log_error("Failed to parse the configuration");
|
||||
return false;
|
||||
}
|
||||
|
||||
pool_config_t *pool = config.pools;
|
||||
while (NULL != pool) {
|
||||
if (NULL == pool->host) {
|
||||
log_error("Hostname not specified for the pool: %s", pool->name);
|
||||
return false;
|
||||
}
|
||||
pool = pool->next;
|
||||
}
|
||||
|
||||
log_info("Loaded the configuration");
|
||||
return true;
|
||||
}
|
||||
|
||||
void config_free() {
|
||||
for (int i = 0; i < option_count(); i++)
|
||||
config_option_free(&config.options[i]);
|
||||
|
||||
pool_config_t *cur = config.pools, *old = NULL;
|
||||
while (NULL != cur) {
|
||||
old = cur;
|
||||
cur = cur->next;
|
||||
|
||||
free(old->host);
|
||||
free(old->name);
|
||||
free(old);
|
||||
}
|
||||
}
|
||||
|
||||
char *config_get_string(char *key) {
|
||||
for (int i = 0; i < option_count(); i++) {
|
||||
if (!eq(config.options[i].key, key) || config.options[i].type != TYPE_STRING)
|
||||
continue;
|
||||
|
||||
if (NULL == config.options[i].val.string)
|
||||
return config.options[i].def.string;
|
||||
|
||||
return config.options[i].val.string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int config_get_integer(char *key) {
|
||||
for (int i = 0; i < option_count(); i++) {
|
||||
if (!eq(config.options[i].key, key) || config.options[i].type != TYPE_INTEGER)
|
||||
continue;
|
||||
|
||||
if (config.options[i].val.integer < 0)
|
||||
return config.options[i].def.integer;
|
||||
|
||||
return config.options[i].val.integer;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
39
src/config.h
Normal file
39
src/config.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum config_type {
|
||||
TYPE_STRING = 0,
|
||||
TYPE_INTEGER = 1,
|
||||
} config_type_t;
|
||||
|
||||
union value {
|
||||
char *string;
|
||||
int integer;
|
||||
};
|
||||
|
||||
typedef struct config_option {
|
||||
config_type_t type;
|
||||
char *key;
|
||||
union value def;
|
||||
union value val;
|
||||
} config_option_t;
|
||||
|
||||
typedef struct pool_config {
|
||||
struct pool_config *next;
|
||||
char *name;
|
||||
char *host;
|
||||
} pool_config_t;
|
||||
|
||||
typedef struct config {
|
||||
config_option_t options[4];
|
||||
pool_config_t *pools;
|
||||
} config_t;
|
||||
|
||||
extern config_t config;
|
||||
|
||||
bool config_load(char *file);
|
||||
void config_free();
|
||||
|
||||
char *config_get_string(char *key);
|
||||
int config_get_integer(char *key);
|
2
src/intl.h
Normal file
2
src/intl.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include <libintl.h>
|
||||
#define _(x) gettext(x)
|
46
src/log.c
Normal file
46
src/log.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
void log_error(char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm tm = *localtime(&now);
|
||||
|
||||
fprintf(stderr,
|
||||
"[%02d/%02d/%d %02d:%02d:%02d] -ERROR- ",
|
||||
tm.tm_mday,
|
||||
tm.tm_mon + 1,
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_hour,
|
||||
tm.tm_hour,
|
||||
tm.tm_sec);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_info(char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm tm = *localtime(&now);
|
||||
|
||||
fprintf(stdout,
|
||||
"[%02d/%02d/%d %02d:%02d:%02d] -INFO- ",
|
||||
tm.tm_mday,
|
||||
tm.tm_mon + 1,
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_hour,
|
||||
tm.tm_hour,
|
||||
tm.tm_sec);
|
||||
vfprintf(stdout, fmt, args);
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
va_end(args);
|
||||
}
|
3
src/log.h
Normal file
3
src/log.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
void log_error(char *fmt, ...);
|
||||
void log_info(char *fmt, ...);
|
161
src/main.c
Normal file
161
src/main.c
Normal file
@ -0,0 +1,161 @@
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
|
||||
* pooler | MatterLinux pool server
|
||||
* MatterLinux 2023-2024 (https://matterlinux.xyz)
|
||||
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
// clang-format on
|
||||
|
||||
#include <errno.h>
|
||||
#include <libmp/all.h>
|
||||
#include <libmp/ctx.h>
|
||||
#include <libmp/error.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "intl.h"
|
||||
#include "log.h"
|
||||
|
||||
bool sync_callback(
|
||||
lm_ctx_t *ctx, lm_pool_t *pool, lm_ctx_sync_state_t state, size_t current, size_t total, void *data) {
|
||||
switch (state) {
|
||||
case SYNC_INFO_SUCCESS:
|
||||
log_info(_("%s: successfuly loaded the pool info"), pool->name);
|
||||
break;
|
||||
|
||||
case SYNC_INFO_FAIL:
|
||||
log_info(_("%s: failed to load the pool info (%s)"), pool->name, lm_strerror());
|
||||
return false;
|
||||
|
||||
case SYNC_LIST_SUCCESS:
|
||||
log_info(_("%s: successfuly loaded the package list"), pool->name);
|
||||
break;
|
||||
|
||||
case SYNC_LIST_FAIL:
|
||||
log_info(_("%s: failed to load the package list (%s)"), pool->name, lm_strerror());
|
||||
return false;
|
||||
|
||||
default:
|
||||
// ignore other callbacks
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
log_error(_("Configuration file not specified"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char *poolsdir = NULL, *addr = NULL, *logfile = NULL;
|
||||
int ret = EXIT_FAILURE;
|
||||
pool_config_t *pool = NULL;
|
||||
size_t pool_count = 0;
|
||||
lm_ctx_t ctx;
|
||||
|
||||
lm_ctx_init(&ctx);
|
||||
|
||||
if (!config_load(argv[1]))
|
||||
goto end;
|
||||
|
||||
if ((logfile = config_get_string("log")) != NULL) {
|
||||
FILE *log = fopen(logfile, "r");
|
||||
|
||||
if (NULL == log)
|
||||
log_error(_("Failed to open the log file: %s"), strerror(errno));
|
||||
|
||||
else {
|
||||
dup2(fileno(log), STDERR_FILENO);
|
||||
dup2(fileno(log), STDOUT_FILENO);
|
||||
fclose(log);
|
||||
}
|
||||
}
|
||||
|
||||
addr = config_get_string("addr");
|
||||
|
||||
if (config_get_integer("threads") <= 0 || config_get_integer("threads") > 1000) {
|
||||
log_error(_("Please specify a valid thread count (1-1000)"));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((poolsdir = config_get_string("dir")) == NULL) {
|
||||
log_error(_("Pool directory not specified"));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!exists(poolsdir) && !can_read(poolsdir)) {
|
||||
log_error(_("Cannot access to the pool directory: %s"), poolsdir);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((pool = config.pools) == NULL) {
|
||||
log_error(_("Please specify at least one pool in the configuration"));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!lm_ctx_set_data(&ctx, poolsdir)) {
|
||||
log_error(_("Failed to use pool directory: %s"), lm_strerror());
|
||||
goto end;
|
||||
}
|
||||
|
||||
while (NULL != pool) {
|
||||
if (NULL == pool->host) {
|
||||
log_error(_("Hostname not specified for pool, skipping: %s"));
|
||||
goto end;
|
||||
}
|
||||
|
||||
char url[strlen(pool->host) + 20];
|
||||
sprintf(url, "mptp://%s", pool->host);
|
||||
|
||||
if (NULL == lm_ctx_pool_add(&ctx, pool->name, url)) {
|
||||
log_error(_("Failed to add pool to the list: %s"), lm_strerror());
|
||||
goto end;
|
||||
}
|
||||
|
||||
pool = pool->next;
|
||||
}
|
||||
|
||||
if ((pool_count = lm_ctx_sync(&ctx, false, sync_callback, NULL)) < 0) {
|
||||
log_error(_("Failed to sync the pools: %s"), lm_strerror());
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (pool_count == 0) {
|
||||
log_error(_("None of the pools is available for serving"));
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_info(pool_count == 1 ? _("Serving %lu pool on %s") : _("Serving %lu pools on %s"), pool_count, addr);
|
||||
|
||||
if (!lm_ctx_serve(&ctx, addr, config_get_integer("threads"))) {
|
||||
log_error(_("Failed to start the server: %s"), lm_strerror());
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
end:
|
||||
lm_ctx_free(&ctx);
|
||||
config_free();
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user