aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Dockerfile6
-rw-r--r--README.md2
-rw-r--r--docker-compose.yml5
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/server.c18
-rw-r--r--src/server.h5
-rw-r--r--src/server_main.c42
-rw-r--r--src/storage.c69
-rw-r--r--src/storage.h29
-rw-r--r--src/storage_sqlite.c67
-rw-r--r--src/storage_sqlite.h19
11 files changed, 249 insertions, 17 deletions
diff --git a/Dockerfile b/Dockerfile
index 295b3ff..cae72f3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,7 @@ ARG install_dir="/app/install"
FROM base AS builder
-RUN apk add --no-cache bash bsd-compat-headers build-base clang cmake libgit2-dev
+RUN apk add --no-cache bash bsd-compat-headers build-base clang cmake libgit2-dev sqlite-dev
ARG C_COMPILER=clang
ARG BUILD_TYPE=Release
@@ -26,7 +26,7 @@ FROM base
LABEL maintainer="Egor Tensin <Egor.Tensin@gmail.com>"
-RUN apk add --no-cache tini libgit2
+RUN apk add --no-cache tini libgit2 sqlite-dev
ARG install_dir
COPY --from=builder ["$install_dir", "$install_dir"]
@@ -35,4 +35,4 @@ ENV PATH="$install_dir/bin:${PATH}"
WORKDIR "$install_dir/bin"
ENTRYPOINT ["/sbin/tini", "--"]
-CMD ["cimple-server"]
+CMD ["cimple-server", "--sqlite", "/var/lib/cimple/cimple.sqlite"]
diff --git a/README.md b/README.md
index 1372add..a9cb052 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Development
-----------
Build using CMake.
-Depends on libgit2.
+Depends on libgit2 and SQLite.
There's a Makefile with useful shortcuts to build the project in the .build/
directory:
diff --git a/docker-compose.yml b/docker-compose.yml
index d6d8460..43796e1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -13,6 +13,8 @@ services:
<<: *common
ports:
- 5556
+ volumes:
+ - db:/var/lib/cimple
worker1:
<<: *common
command: ["cimple-worker"]
@@ -27,3 +29,6 @@ services:
depends_on: [server]
entrypoint: ["cimple-client"]
restart: 'no'
+
+volumes:
+ db:
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c647937..da1f7b1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -23,8 +23,10 @@ add_my_executable(server server_main.c server.c
msg.c
net.c
signal.c
+ storage.c
+ storage_sqlite.c
tcp_server.c)
-target_link_libraries(server PRIVATE pthread)
+target_link_libraries(server PRIVATE pthread sqlite3)
add_my_executable(client client_main.c client.c
msg.c
diff --git a/src/server.c b/src/server.c
index 6139596..c8c2e0d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -5,6 +5,8 @@
#include "log.h"
#include "msg.h"
#include "signal.h"
+#include "storage.h"
+#include "storage_sqlite.h"
#include "tcp_server.h"
#include <pthread.h>
@@ -13,6 +15,7 @@
int server_create(struct server *server, const struct settings *settings)
{
+ struct storage_settings storage_settings;
int ret = 0;
ret = pthread_mutex_init(&server->server_mtx, NULL);
@@ -29,14 +32,26 @@ int server_create(struct server *server, const struct settings *settings)
server->stopping = 0;
- ret = tcp_server_create(&server->tcp_server, settings->port);
+ ret = storage_settings_create_sqlite(&storage_settings, settings->sqlite_path);
+ if (ret < 0)
+ goto destroy_cv;
+
+ ret = storage_create(&server->storage, &storage_settings);
+ storage_settings_destroy(&storage_settings);
if (ret < 0)
goto destroy_cv;
+ ret = tcp_server_create(&server->tcp_server, settings->port);
+ if (ret < 0)
+ goto destroy_storage;
+
ci_queue_create(&server->ci_queue);
return ret;
+destroy_storage:
+ storage_destroy(&server->storage);
+
destroy_cv:
pthread_errno_if(pthread_cond_destroy(&server->server_cv), "pthread_cond_destroy");
@@ -53,6 +68,7 @@ void server_destroy(struct server *server)
ci_queue_destroy(&server->ci_queue);
tcp_server_destroy(&server->tcp_server);
+ storage_destroy(&server->storage);
pthread_errno_if(pthread_cond_destroy(&server->server_cv), "pthread_cond_destroy");
pthread_errno_if(pthread_mutex_destroy(&server->server_mtx), "pthread_mutex_destroy");
}
diff --git a/src/server.h b/src/server.h
index ebd88a1..ba2a52f 100644
--- a/src/server.h
+++ b/src/server.h
@@ -2,12 +2,15 @@
#define __SERVER_H__
#include "ci_queue.h"
+#include "storage.h"
#include "tcp_server.h"
#include <pthread.h>
struct settings {
const char *port;
+
+ const char *sqlite_path;
};
struct server {
@@ -16,6 +19,8 @@ struct server {
int stopping;
+ struct storage storage;
+
struct tcp_server tcp_server;
struct ci_queue ci_queue;
diff --git a/src/server_main.c b/src/server_main.c
index b77a681..e2d811c 100644
--- a/src/server_main.c
+++ b/src/server_main.c
@@ -7,18 +7,32 @@
static struct settings default_settings()
{
- struct settings settings = {DEFAULT_PORT};
+ struct settings settings = {DEFAULT_PORT, NULL};
return settings;
}
-static void print_usage(const char *argv0)
+static void exit_with_usage(int ec, const char *argv0)
{
- printf("usage: %s [-h|--help] [-V|--version] [-p|--port PORT]\n", argv0);
+ FILE *dest = stdout;
+ if (ec)
+ dest = stderr;
+
+ fprintf(dest, "usage: %s [-h|--help] [-V|--version] [-p|--port PORT] [-s|--sqlite PATH]\n",
+ argv0);
+ exit(ec);
+}
+
+static void exit_with_usage_err(const char *argv0, const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "usage error: %s\n", msg);
+ exit_with_usage(1, argv0);
}
-static void print_version()
+static void exit_with_version()
{
printf("%s\n", VERSION);
+ exit(0);
}
static int parse_settings(struct settings *settings, int argc, char *argv[])
@@ -31,29 +45,35 @@ static int parse_settings(struct settings *settings, int argc, char *argv[])
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{"port", required_argument, 0, 'p'},
+ {"sqlite", required_argument, 0, 's'},
{0, 0, 0, 0},
};
- while ((opt = getopt_long(argc, argv, "hVp:", long_options, &longind)) != -1) {
+ while ((opt = getopt_long(argc, argv, "hVp:s:", long_options, &longind)) != -1) {
switch (opt) {
case 'h':
- print_usage(argv[0]);
- exit(0);
+ exit_with_usage(0, argv[0]);
break;
case 'V':
- print_version();
- exit(0);
+ exit_with_version();
break;
case 'p':
settings->port = optarg;
break;
+ case 's':
+ settings->sqlite_path = optarg;
+ break;
default:
- print_usage(argv[0]);
- exit(1);
+ exit_with_usage(1, argv[0]);
break;
}
}
+ if (!settings->sqlite_path) {
+ exit_with_usage_err(argv[0], "must specify the path to a SQLite database");
+ return -1;
+ }
+
return 0;
}
diff --git a/src/storage.c b/src/storage.c
new file mode 100644
index 0000000..eac3e2d
--- /dev/null
+++ b/src/storage.c
@@ -0,0 +1,69 @@
+#include "storage.h"
+#include "log.h"
+#include "storage_sqlite.h"
+
+typedef void (*storage_settings_destroy_t)(const struct storage_settings *);
+typedef int (*storage_create_t)(struct storage *, const struct storage_settings *);
+typedef void (*storage_destroy_t)(struct storage *);
+
+struct storage_api {
+ storage_settings_destroy_t destroy_settings;
+ storage_create_t create;
+ storage_destroy_t destroy;
+};
+
+static const struct storage_api apis[] = {
+ {
+ storage_settings_destroy_sqlite,
+ storage_create_sqlite,
+ storage_destroy_sqlite,
+ },
+};
+
+static size_t numof_apis()
+{
+ return sizeof(apis) / sizeof(apis[0]);
+}
+
+static const struct storage_api *get_api(enum storage_type type)
+{
+ if (type < 0)
+ goto invalid_type;
+ if ((size_t)type > numof_apis())
+ goto invalid_type;
+
+ return &apis[type];
+
+invalid_type:
+ log_err("Unsupported storage type: %d\n", type);
+ return NULL;
+}
+
+void storage_settings_destroy(const struct storage_settings *settings)
+{
+ const struct storage_api *api = get_api(settings->type);
+ if (!api)
+ return;
+ api->destroy_settings(settings);
+}
+
+int storage_create(struct storage *storage, const struct storage_settings *settings)
+{
+ int ret = 0;
+ const struct storage_api *api = get_api(settings->type);
+ if (!api)
+ return -1;
+ ret = api->create(storage, settings);
+ if (ret < 0)
+ return ret;
+ storage->type = settings->type;
+ return ret;
+}
+
+void storage_destroy(struct storage *storage)
+{
+ const struct storage_api *api = get_api(storage->type);
+ if (!api)
+ return;
+ api->destroy(storage);
+}
diff --git a/src/storage.h b/src/storage.h
new file mode 100644
index 0000000..1473325
--- /dev/null
+++ b/src/storage.h
@@ -0,0 +1,29 @@
+#ifndef __STORAGE_H__
+#define __STORAGE_H__
+
+#include "storage_sqlite.h"
+
+enum storage_type {
+ STORAGE_TYPE_SQLITE,
+};
+
+struct storage_settings {
+ enum storage_type type;
+ union {
+ struct storage_settings_sqlite sqlite;
+ };
+};
+
+void storage_settings_destroy(const struct storage_settings *);
+
+struct storage {
+ enum storage_type type;
+ union {
+ struct storage_sqlite *sqlite;
+ };
+};
+
+int storage_create(struct storage *, const struct storage_settings *);
+void storage_destroy(struct storage *);
+
+#endif
diff --git a/src/storage_sqlite.c b/src/storage_sqlite.c
new file mode 100644
index 0000000..80cc294
--- /dev/null
+++ b/src/storage_sqlite.c
@@ -0,0 +1,67 @@
+#include "storage_sqlite.h"
+#include "log.h"
+#include "storage.h"
+
+#include <sqlite3.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#define sqlite_errno(var, fn) \
+ do { \
+ log_err("%s: %s\n", fn, sqlite3_errstr(var)); \
+ var = -var; \
+ } while (0)
+
+#define sqlite_errno_if(expr, fn) \
+ do { \
+ int CONCAT(ret, __LINE__) = expr; \
+ if (CONCAT(ret, __LINE__)) \
+ sqlite_errno(CONCAT(ret, __LINE__), fn); \
+ } while (0)
+
+int storage_settings_create_sqlite(struct storage_settings *settings, const char *path)
+{
+ settings->sqlite.path = strdup(path);
+ if (!settings->sqlite.path) {
+ log_errno("strdup");
+ return -1;
+ }
+
+ settings->type = STORAGE_TYPE_SQLITE;
+ return 0;
+}
+
+void storage_settings_destroy_sqlite(const struct storage_settings *settings)
+{
+ free(settings->sqlite.path);
+}
+
+struct storage_sqlite {
+ sqlite3 *db;
+};
+
+int storage_create_sqlite(struct storage *storage, const struct storage_settings *settings)
+{
+ int ret = 0;
+
+ storage->sqlite = malloc(sizeof(storage->sqlite));
+ if (!storage->sqlite) {
+ log_errno("malloc");
+ return -1;
+ }
+
+ ret = sqlite3_open(settings->sqlite.path, &storage->sqlite->db);
+ if (ret) {
+ sqlite_errno(ret, "sqlite3_open");
+ return ret;
+ }
+
+ return 0;
+}
+
+void storage_destroy_sqlite(struct storage *storage)
+{
+ sqlite_errno_if(sqlite3_close(storage->sqlite->db), "sqlite3_close");
+ free(storage->sqlite);
+}
diff --git a/src/storage_sqlite.h b/src/storage_sqlite.h
new file mode 100644
index 0000000..e565052
--- /dev/null
+++ b/src/storage_sqlite.h
@@ -0,0 +1,19 @@
+#ifndef __STORAGE_SQLITE_H__
+#define __STORAGE_SQLITE_H__
+
+struct storage_settings;
+
+struct storage_settings_sqlite {
+ char *path;
+};
+
+struct storage;
+struct storage_sqlite;
+
+int storage_settings_create_sqlite(struct storage_settings *, const char *path);
+void storage_settings_destroy_sqlite(const struct storage_settings *);
+
+int storage_create_sqlite(struct storage *, const struct storage_settings *);
+void storage_destroy_sqlite(struct storage *);
+
+#endif