diff options
-rw-r--r-- | Dockerfile | 6 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | docker-compose.yml | 5 | ||||
-rw-r--r-- | src/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/server.c | 18 | ||||
-rw-r--r-- | src/server.h | 5 | ||||
-rw-r--r-- | src/server_main.c | 42 | ||||
-rw-r--r-- | src/storage.c | 69 | ||||
-rw-r--r-- | src/storage.h | 29 | ||||
-rw-r--r-- | src/storage_sqlite.c | 67 | ||||
-rw-r--r-- | src/storage_sqlite.h | 19 |
11 files changed, 249 insertions, 17 deletions
@@ -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"] @@ -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 |