summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2018-09-11 11:28:34 +0200
committerJose Bollo <jose.bollo@iot.bzh>2018-09-17 13:17:56 +0200
commit7ae3477d84ae13c5e9ef9bb1980a8f70f8b7e9bf (patch)
tree46c1ddda9a4dcc4782ceab8deb2f891ad3b0f2c8
parent11654afcb5753a54a033db12e1ed4a19b3f7c86e (diff)
Make it work for AGL
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
-rw-r--r--CMakeLists.txt27
-rw-r--r--cynara.initial4
-rw-r--r--src/CMakeLists.txt29
-rw-r--r--src/cache.c241
-rw-r--r--src/cache.h17
-rw-r--r--src/cyn.c94
-rw-r--r--src/cyn.h22
-rw-r--r--src/db.c110
-rw-r--r--src/db.h32
-rw-r--r--src/fbuf.c162
-rw-r--r--src/fbuf.h40
-rw-r--r--src/lib-compat.c43
-rw-r--r--src/main-cynarad.c488
-rw-r--r--src/prot.c17
-rw-r--r--src/prot.h17
-rw-r--r--src/queue.c17
-rw-r--r--src/queue.h17
-rw-r--r--src/rcyn-client.c72
-rw-r--r--src/rcyn-client.h17
-rw-r--r--src/rcyn-protocol.c17
-rw-r--r--src/rcyn-protocol.h17
-rw-r--r--src/rcyn-server.c256
-rw-r--r--src/rcyn-server.h50
-rw-r--r--src/socket.c2
-rw-r--r--src/test-lib-compat.c19
-rw-r--r--systemd/CMakeLists.txt32
-rw-r--r--systemd/cynara-admin.socket.in2
-rw-r--r--systemd/cynara-check.socket.in2
-rw-r--r--systemd/cynara.service11
29 files changed, 1591 insertions, 283 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a3c9f1..d4ed7c5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,13 +23,16 @@ PROJECT(cynara C)
SET(PROJECT_NAME "Cynara")
SET(PROJECT_PRETTY_NAME "Permission database")
SET(PROJECT_DESCRIPTION "Secured permission database for applications")
-SET(PROJECT_VERSION "1.99.RC1")
+SET(PROJECT_VERSION "1.99.99")
set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/cynara.git;a=summary")
INCLUDE(FindPkgConfig)
INCLUDE(CheckIncludeFiles)
INCLUDE(CheckLibraryExists)
INCLUDE(GNUInstallDirs)
+if(NOT CMAKE_INSTALL_FULL_RUNSTATEDIR)
+ set(CMAKE_INSTALL_FULL_RUNSTATEDIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/run")
+endif()
INCLUDE(CTest)
###########################################################################
@@ -41,6 +44,13 @@ set(CYNARA_SOVERSION 1.99)
add_definitions(-DCYNARA_VERSION="${CYNARA_VERSION}")
set(SYSTEMD ON CACHE BOOL "should use systemd")
+set(DEFAULT_DB_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/cynara"
+ CACHE PATH "directory path of the database")
+set(DEFAULT_SOCKET_DIR "${CMAKE_INSTALL_FULL_RUNSTATEDIR}"
+ CACHE PATH "directory path of the sockets")
+set(DEFAULT_CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/security"
+ CACHE PATH "directory of configuration")
+set(DEFAULT_INIT_FILE "${DEFAULT_CONFIG_DIR}/cynara.initial")
###########################################################################
@@ -65,18 +75,15 @@ set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage")
if(SYSTEMD)
PKG_CHECK_MODULES(libsystemd REQUIRED libsystemd>=222)
-endif()
-
-if(SYSTEMD)
- set(SOCKET_DIR "/run/platform"
- CACHE PATH "path of the socket system directories")
- set(SYSTEMD_UNIT_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/systemd/system"
- CACHE PATH "Path to systemd system unit files")
- set(CHECK_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.check")
- set(ADMIN_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.admin")
add_subdirectory(systemd)
endif()
+
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(pkgconfig)
+install(FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/cynara.initial
+ DESTINATION
+ ${DEFAULT_CONFIG_DIR}
+)
diff --git a/cynara.initial b/cynara.initial
new file mode 100644
index 0000000..96054d3
--- /dev/null
+++ b/cynara.initial
@@ -0,0 +1,4 @@
+# initial database for cynara
+System * * * 1
+User * * * 1
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fa3724c..4363234 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -20,6 +20,7 @@ set(SERVER_SOURCES
cyn.c
db.c
fbuf.c
+ main-cynarad.c
prot.c
queue.c
rcyn-protocol.c
@@ -40,6 +41,14 @@ set(LIB_SOURCES
# build and install cynarad
###########################################
add_executable(cynarad ${SERVER_SOURCES})
+target_compile_definitions(cynarad PRIVATE
+ DEFAULT_DB_DIR="${DEFAULT_DB_DIR}"
+ DEFAULT_SOCKET_DIR="${DEFAULT_SOCKET_DIR}"
+ DEFAULT_INIT_FILE="${DEFAULT_INIT_FILE}"
+ RCYN_DEFAULT_CHECK_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.check"
+ RCYN_DEFAULT_ADMIN_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.admin"
+)
+target_link_libraries(cynarad cap)
install(TARGETS cynarad
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
@@ -47,6 +56,10 @@ install(TARGETS cynarad
# build and install libcynara
###########################################
ADD_LIBRARY(cynara SHARED ${LIB_SOURCES})
+target_compile_definitions(cynara PRIVATE
+ RCYN_DEFAULT_CHECK_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.check"
+ RCYN_DEFAULT_ADMIN_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.admin"
+)
SET_TARGET_PROPERTIES(cynara PROPERTIES
VERSION ${CYNARA_VERSION}
SOVERSION ${CYNARA_SOVERSION})
@@ -71,25 +84,9 @@ INSTALL(TARGETS test-cynara
###########################################
if(SYSTEMD)
- target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="sd:check")
- target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="sd:admin")
target_compile_definitions(cynarad PRIVATE WITH_SYSTEMD_ACTIVATION)
target_link_libraries(cynarad ${libsystemd_LDFLAGS} ${libsystemd_LINK_LIBRARIES})
target_include_directories(cynarad PRIVATE ${libsystemd_INCLUDE_DIRS})
target_compile_options(cynarad PRIVATE ${libsystemd_CFLAGS})
-else()
- if(CHECK_SOCKET_SPEC)
- target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}")
- endif()
- if(ADMIN_SOCKET_SPEC)
- target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}")
- endif()
-endif()
-
-if(CHECK_SOCKET_SPEC)
- target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}")
-endif()
-if(ADMIN_SOCKET_SPEC)
- target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}")
endif()
diff --git a/src/cache.c b/src/cache.c
index c2db00d..8d15728 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -1,17 +1,48 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
+#include <ctype.h>
#include "cache.h"
+/**
+ * The cache structure is a blob of memory ('content')
+ * of 'count' bytes of only 'used' bytes.
+ * That blob containts at sequence of records of variable length
+ * Each records holds the following values in that given order:
+ * - length: 2 bytes unsigned integer LSB first, MSB second
+ * - hit count: 1 byte unsigned integer
+ * - value: 1 byte unsigned integer
+ * - client: zero terminated string
+ * - session: zero terminated string
+ * - user: zero terminated string
+ * - permission: zero terminated string
+ * -
+ */
struct cache
{
- uint32_t begin;
uint32_t used;
uint32_t count;
- char content[1];
+ uint8_t content[1];
};
static
@@ -20,57 +51,129 @@ lenat(
cache_t *cache,
uint32_t pos
) {
- uint32_t p, n;
- char c;
+ return ((uint32_t)cache->content[pos]) | (((uint32_t)cache->content[pos + 1]) << 8);
+}
- p = pos + 1;
- if (p >= cache->count)
- p -= cache->count;
- n = 4;
- while (n--) {
- do {
- c = cache->content[p++];
- if (p >= cache->count)
- p -= cache->count;
- } while(c);
- }
- return (p > pos ? p : (p + cache->count)) - pos;
+static
+void
+drop_at(
+ cache_t *cache,
+ uint32_t pos
+) {
+ uint32_t e, l;
+
+ l = lenat(cache, pos);
+ e = pos + l;
+ cache->used -= l;
+ if (cache->used > e)
+ memmove(&cache->content[pos], &cache->content[e], cache->used - e);
}
static
void
-drop_one(
+drop_lre(
cache_t *cache
) {
- uint32_t l = lenat(cache, cache->begin);
- cache->used -= l;
- cache->begin += l;
- if (cache->begin > cache->count)
- cache->begin -= cache->count;
+ uint32_t found = 0, iter = 0;
+ uint8_t hmin = 255, hint;
+
+ while (iter < cache->used) {
+ hint = cache->content[iter + 2];
+ if (hint < hmin)
+ found = iter;
+ iter += lenat(cache, iter);
+ }
+ if (found < cache->used)
+ drop_at(cache, found);
}
static
void
-addc(
+hit(
cache_t *cache,
- char c
+ uint32_t pos
) {
- uint32_t pos;
- if (cache->used == cache->count)
- drop_one(cache);
- pos = cache->begin + cache->used++;
- if (pos > cache->count)
- pos -= cache->count;
- cache->content[pos < cache->count ? pos : pos - cache->count] = c;
+ uint32_t iter = 0;
+ uint8_t hint;
+
+ while (iter < cache->used) {
+ if (iter == pos)
+ hint = 255;
+ else {
+ hint = cache->content[iter + 2];
+ if (hint)
+ hint--;
+ }
+ cache->content[iter + 2] = hint;
+ iter += lenat(cache, iter);
+ }
}
static
-void
-adds(
+const char*
+cmpi(
+ const char *head,
+ const char *other
+) {
+ char c;
+ while(toupper(c = *head++) == toupper(*other++))
+ if (!c)
+ return head;
+ return 0;
+}
+
+static
+const char*
+cmp(
+ const char *head,
+ const char *other
+) {
+ char c;
+ while((c = *head++) == *other++)
+ if (!c)
+ return head;
+ return 0;
+}
+
+
+static
+int
+match(
+ const char *head,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ head = cmp(head, client);
+ if (head)
+ head = cmp(head, session);
+ if (head)
+ head = cmp(head, user);
+ if (head)
+ head = cmpi(head, permission);
+ return !!head;
+}
+
+static
+uint32_t
+search(
cache_t *cache,
- const char *s
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
) {
- do { addc(cache, *s); } while(*s++);
+ char *txt;
+ uint32_t iter = 0;
+
+ while (iter < cache->used) {
+ txt = (char*)&cache->content[iter + 4];
+ if (match(txt, client, session, user, permission))
+ return iter;
+ iter += lenat(cache, iter);
+ }
+ return iter;
}
int
@@ -82,17 +185,35 @@ cache_put(
const char *permission,
int value
) {
- if (cache == NULL
- || strlen(client) + strlen(session)
- + strlen(user) + strlen(permission)
- + 5 > cache->count)
+ uint32_t pos;
+ size_t size, scli, sses, susr, sper;
+
+ if (cache == NULL || value < 0 || value > 255)
return -EINVAL;
- addc(cache, (char)value);
- adds(cache, client);
- adds(cache, session);
- adds(cache, user);
- adds(cache, permission);
+ pos = search(cache, client, session, user, permission);
+ if (pos < cache->used)
+ cache->content[pos + 3] = (uint8_t)value;
+ else {
+ scli = strlen(client);
+ sses = strlen(session);
+ susr = strlen(user);
+ sper = strlen(permission);
+ size = scli + sses + susr + sper + 8;
+ if (size > 65535)
+ return -EINVAL;
+ if (size > cache->count)
+ return -ENOMEM;
+ while(cache->used + (uint32_t)size > cache->count)
+ drop_lre(cache);
+ pos = cache->used;
+ cache->content[pos + 0] = (uint8_t)(size & 255);
+ cache->content[pos + 1] = (uint8_t)((size >> 8) & 255);
+ cache->content[pos + 2] = (uint8_t)255;
+ cache->content[pos + 3] = (uint8_t)value;
+ stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)&cache->content[pos + 4], client), session), user), permission);
+ cache->used += (uint32_t)size;
+ }
return 0;
}
@@ -104,6 +225,13 @@ cache_search(
const char *user,
const char *permission
) {
+ uint32_t pos;
+
+ pos = search(cache, client, session, user, permission);
+ if (pos < cache->used) {
+ hit(cache, pos);
+ return (int)cache->content[pos + 3];
+ }
return -ENOENT;
}
@@ -111,10 +239,8 @@ void
cache_clear(
cache_t *cache
) {
- if (cache) {
+ if (cache)
cache->used = 0;
- cache->begin = 0;
- }
}
int
@@ -124,29 +250,18 @@ cache_resize(
) {
cache_t *c = *cache, *nc;
- while (c && c->used > newsize)
- drop_one(c);
+ if (c)
+ while (c->used > newsize)
+ drop_lre(c);
- nc = malloc(newsize - 1 + sizeof *c);
+ nc = realloc(c, newsize - 1 + sizeof *c);
if (nc == NULL)
return -ENOMEM;
- nc->begin = 0;
nc->count = newsize;
- if (!c || c->used == 0)
+ if (!c)
nc->used = 0;
- else {
- if (c->begin + c->used <= c->count)
- memcpy(&nc->content[0], &c->content[c->begin], c->used);
- else {
- memcpy(&nc->content[0], &c->content[c->begin], c->count - c->begin);
- memcpy(&nc->content[c->count - c->begin], &c->content[0], c->used + c->begin - c->count);
- }
-
- nc->used = c->used;
- }
*cache = nc;
- free(c);
return 0;
}
diff --git a/src/cache.h b/src/cache.h
index 2f3a9a1..5cd827b 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#pragma once
struct cache;
diff --git a/src/cyn.c b/src/cyn.c
index 4eb6d9e..85deb4e 100644
--- a/src/cyn.c
+++ b/src/cyn.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#define _GNU_SOURCE
@@ -68,27 +85,15 @@ static
int
changed(
) {
- int rc = db_sync();
+ int rc;
struct callback *c;
+ rc = db_sync();
for (c = observers; c ; c = c->next)
c->callback(c->closure);
return rc;
}
-int
-cyn_init(
-) {
- /* TODO: paths? */
- int rc = db_open("/var/lib/cynara/cynara.names", "/var/lib/cynara/cynara.rules");
- if (rc == 0 && db_is_empty()) {
- /* TODO: init? */
- rc = db_set("System", "*", "*", "*", 1);
- db_sync();
- }
- return rc;
-}
-
/** enter critical recoverable section */
int
cyn_enter(
@@ -155,10 +160,17 @@ cyn_leave(
return -EPERM;
lock = &lock;
- if (commit)
- rc = queue_play() ?: changed();
- else
+ if (!commit)
rc = 0;
+ else {
+ rc = queue_play();
+ if (rc < 0)
+ db_recover();
+ else {
+ rc = db_backup();
+ changed();
+ }
+ }
queue_clear();
e = awaiters;
@@ -187,10 +199,9 @@ cyn_set(
const char *permission,
uint32_t value
) {
- if (lock && lock != &lock)
- return queue_set(client, session, user, permission, value);
-
- return db_set(client, session, user, permission, value) ?: changed();
+ if (!lock)
+ return -EPERM;
+ return queue_set(client, session, user, permission, value);
}
int
@@ -200,10 +211,27 @@ cyn_drop(
const char *user,
const char *permission
) {
- if (lock && lock != &lock)
- return queue_drop(client, session, user, permission);
+ if (!lock)
+ return -EPERM;
+ return queue_drop(client, session, user, permission);
+}
- return db_drop(client, session, user, permission) ?: changed();
+void
+cyn_list(
+ void *closure,
+ void (*callback)(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value),
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ db_for_all(closure, callback, client, session, user, permission);
}
int
@@ -224,24 +252,6 @@ cyn_test(
return rc;
}
-void
-cyn_list(
- void *closure,
- void (*callback)(
- void *closure,
- const char *client,
- const char *session,
- const char *user,
- const char *permission,
- uint32_t value),
- const char *client,
- const char *session,
- const char *user,
- const char *permission
-) {
- db_for_all(closure, callback, client, session, user, permission);
-}
-
int
cyn_check_async(
void (*check_cb)(void *closure, uint32_t value),
diff --git a/src/cyn.h b/src/cyn.h
index 0a061c2..df08a5a 100644
--- a/src/cyn.h
+++ b/src/cyn.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#pragma once
@@ -6,11 +23,6 @@
#define ASK 2
#define DEFAULT DENY
-extern
-int
-cyn_init(
-);
-
/** enter critical recoverable section */
extern
int
diff --git a/src/db.c b/src/db.c
index c7efdca..f5f46da 100644
--- a/src/db.c
+++ b/src/db.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#define _GNU_SOURCE
@@ -7,9 +24,9 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdalign.h>
+#include <stdio.h>
#include <string.h>
#include <errno.h>
-#include <syslog.h>
#include "fbuf.h"
#include "db.h"
@@ -49,6 +66,10 @@ typedef struct session session_t;
* - rules: the rules based on name indexes as 32bits indexes
* These files are normally in /var/lib/cynara
*/
+#if !defined(DEFAULT_DB_DIR)
+# define DEFAULT_DB_DIR "/var/lib/cynara"
+#endif
+static const char db_default_directory[] = DEFAULT_DB_DIR;
/** the file for the names */
static fbuf_t fnames;
@@ -161,7 +182,7 @@ db_get_name_index(
if (!(up & 1023)) {
p = realloc(names_sorted, (up + 1024) * sizeof *names_sorted);
if (p == NULL) {
- syslog(LOG_ERR, "out of memory");
+ fprintf(stderr, "out of memory");
return -1;
}
names_sorted = p;
@@ -199,7 +220,7 @@ init_names(
p = realloc(ns, all * sizeof *ns);
if (p == NULL) {
free(ns);
- syslog(LOG_ERR, "out of memory");
+ fprintf(stderr, "out of memory");
goto error;
}
ns = p;
@@ -228,7 +249,7 @@ init_names(
return 0;
bad_file:
- syslog(LOG_ERR, "bad file %s", fnames.name);
+ fprintf(stderr, "bad file %s", fnames.name);
errno = ENOEXEC;
error:
return -1;
@@ -392,21 +413,53 @@ init_rules(
sessions.count = (frules.used - uuidlen) / sizeof *sessions.rules;
}
+/** open a fbuf */
+static
+int
+open_identify(
+ fbuf_t *fb,
+ const char *directory,
+ const char *name,
+ const char *id,
+ uint32_t idlen
+) {
+ int rc;
+ char *file, *backup;
+
+ rc = asprintf(&file, "%s/%s", directory, name);
+ if (rc < 0)
+ rc = -ENOMEM;
+ else {
+ rc = asprintf(&backup, "%s~", file);
+ if (rc < 0)
+ rc = -ENOMEM;
+ else {
+ rc = fbuf_open_identify(fb, file, backup, id, idlen);
+ free(backup);
+ }
+ free(file);
+ }
+ return rc;
+}
+
/** open the database for files 'names' and 'rules' (can be NULL) */
int
db_open(
- const char *names,
- const char *rules
+ const char *directory
) {
int rc;
+ /* provide default directory */
+ if (directory == NULL)
+ directory = db_default_directory;
+
/* open the names */
- rc = fbuf_open_identify(&fnames, names ?: "cynara.names", uuid_names_v1, uuidlen);
+ rc = open_identify(&fnames, directory, "cynara.names", uuid_names_v1, uuidlen);
if (rc < 0)
goto error;
/* open the rules */
- rc = fbuf_open_identify(&frules, rules ?: "cynara.rules", uuid_rules_v1, uuidlen);
+ rc = open_identify(&frules, directory, "cynara.rules", uuid_rules_v1, uuidlen);
if (rc < 0)
goto error;
@@ -450,6 +503,47 @@ db_sync(
return rc;
}
+/** make a backup of the database */
+int
+db_backup(
+) {
+ int rc;
+
+ assert(fnames.name && frules.name);
+ rc = fbuf_backup(&fnames);
+ if (rc == 0)
+ rc = fbuf_backup(&frules);
+ return rc;
+}
+
+/** recover the database from latest backup */
+int
+db_recover(
+) {
+ int rc;
+
+ assert(fnames.name && frules.name);
+
+ rc = fbuf_recover(&fnames);
+ if (rc < 0)
+ goto error;
+
+ rc = fbuf_recover(&frules);
+ if (rc < 0)
+ goto error;
+
+ rc = init_names();
+ if (rc < 0)
+ goto error;
+
+ init_rules();
+ return 0;
+error:
+ fprintf(stderr, "db recovering impossible: %m");
+ exit(5);
+ return rc;
+}
+
/** enumerate */
void
db_for_all(
diff --git a/src/db.h b/src/db.h
index ce56e8c..0639833 100644
--- a/src/db.h
+++ b/src/db.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#pragma once
#define MAX_NAME_LENGTH 32767
@@ -6,8 +23,7 @@
extern
int
db_open(
- const char *names,
- const char *rules
+ const char *directory
);
/** close the database */
@@ -50,6 +66,18 @@ db_get_name_index(
bool needed
);
+/** make a backup of the database */
+extern
+int
+db_backup(
+);
+
+/** recover the database from latest backup */
+extern
+int
+db_recover(
+);
+
/** enumerate */
extern
void
diff --git a/src/fbuf.c b/src/fbuf.c
index 6d4d41c..c1d4475 100644
--- a/src/fbuf.c
+++ b/src/fbuf.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#define _GNU_SOURCE
@@ -6,6 +23,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
@@ -14,7 +32,6 @@
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <syslog.h>
#include "fbuf.h"
@@ -31,15 +48,27 @@ get_asz(
int
fbuf_open(
fbuf_t *fb,
- const char *name
+ const char *name,
+ const char *backup
) {
struct stat st;
- int fd, rc;
+ int fd, fdback, rc;
uint32_t sz, asz;
void *buffer;
+ /* open the backup */
+ if (backup == NULL)
+ fdback = -1;
+ else {
+ fdback = open(backup, O_RDWR|O_CREAT, 0600);
+ if (fdback < 0)
+ goto error;
+ rc = flock(fdback, LOCK_EX|LOCK_NB);
+ if (rc < 0)
+ goto error;
+ }
+
/* open the file */
- //fd = open(name, O_RDWR|O_CREAT|O_SYNC|O_DIRECT, 0600);
fd = open(name, O_RDWR|O_CREAT, 0600);
if (fd < 0)
goto error;
@@ -71,12 +100,17 @@ fbuf_open(
if (read(fd, buffer, (size_t)sz) != (ssize_t)sz)
goto error4;
+ /* save name */
+ fb->name = strdup(name);
+ if (fb->name == NULL)
+ goto error4;
+
/* done */
- fb->name = name;
fb->buffer = buffer;
fb->saved = fb->used = fb->size = sz;
fb->capacity = asz;
fb->fd = fd;
+ fb->backup = fdback;
return 0;
error4:
@@ -86,9 +120,14 @@ error3:
error2:
close(fd);
error:
- syslog(LOG_ERR, "can't open file %s: %m", name);
+ rc = -errno;
+ fprintf(stderr, "can't open file %s: %m", name);
+ if (fdback >= 0) {
+ flock(fdback, LOCK_UN);
+ close(fdback);
+ }
memset(fb, 0, sizeof *fb);
- return -errno;
+ return rc;
}
/** close the file 'fb' */
@@ -96,9 +135,14 @@ void
fbuf_close(
fbuf_t *fb
) {
+ free(fb->name);
free(fb->buffer);
flock(fb->fd, LOCK_UN);
close(fb->fd);
+ if (fb->backup >= 0) {
+ flock(fb->backup, LOCK_UN);
+ close(fb->backup);
+ }
memset(fb, 0, sizeof *fb);
}
@@ -110,25 +154,19 @@ fbuf_write(
uint32_t count,
uint32_t offset
) {
- off_t rco;
ssize_t rcs;
/* don't call me for nothing */
assert(count);
- /* set write position */
- rco = lseek(fb->fd, (off_t)offset, SEEK_SET);
- if (rco != (off_t)offset)
- goto error;
-
/* effective write */
- rcs = write(fb->fd, buffer, (size_t)count);
+ rcs = pwrite(fb->fd, buffer, (size_t)count, (off_t)offset);
if (rcs != (ssize_t)count)
goto error;
return 0;
error:
- syslog(LOG_ERR, "write of file %s failed: %m", fb->name);
+ fprintf(stderr, "write of file %s failed: %m", fb->name);
return -errno;
}
@@ -167,7 +205,7 @@ fbuf_sync(
return 0;
error:
- syslog(LOG_ERR, "sync of file %s failed: %m", fb->name);
+ fprintf(stderr, "sync of file %s failed: %m", fb->name);
return -errno;
}
@@ -184,7 +222,7 @@ fbuf_ensure_capacity(
capacity = get_asz(count);
buffer = realloc(fb->buffer, capacity);
if (buffer == NULL) {
- syslog(LOG_ERR, "alloc %u for file %s failed: %m", capacity, fb->name);
+ fprintf(stderr, "alloc %u for file %s failed: %m", capacity, fb->name);
return -ENOMEM;
}
fb->buffer = buffer;
@@ -254,7 +292,7 @@ fbuf_identify(
/* bad identification */
errno = ENOKEY;
- syslog(LOG_ERR, "identification of file %s failed: %m", fb->name);
+ fprintf(stderr, "identification of file %s failed: %m", fb->name);
return -ENOKEY;
}
@@ -263,12 +301,13 @@ int
fbuf_open_identify(
fbuf_t *fb,
const char *name,
+ const char *backup,
const char *id,
uint32_t idlen
) {
int rc;
- rc = fbuf_open(fb, name);
+ rc = fbuf_open(fb, name, backup);
if (rc == 0) {
rc = fbuf_identify(fb, id, idlen);
if (rc < 0)
@@ -277,3 +316,88 @@ fbuf_open_identify(
return rc;
}
+
+/* On versions of glibc before 2.27, we must invoke copy_file_range()
+ using syscall(2) */
+#include <features.h>
+#if (__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 27)
+#include <syscall.h>
+static
+loff_t
+copy_file_range(
+ int fd_in,
+ loff_t *off_in,
+ int fd_out,
+ loff_t *off_out,
+ size_t len,
+ unsigned int flags
+) {
+ loff_t rc;
+
+ rc = syscall(__NR_copy_file_range, fd_in, off_in, fd_out,
+ off_out, len, flags);
+ return rc;
+}
+#endif
+
+
+/** make a backup */
+int
+fbuf_backup(
+ fbuf_t *fb
+) {
+ int rc;
+ size_t sz;
+ ssize_t wsz;
+ loff_t ino, outo;
+
+ ino = outo = 0;
+ sz = fb->size;
+ for (;;) {
+ wsz = copy_file_range(fb->fd, &ino, fb->backup, &outo, sz, 0);
+ if (wsz < 0) {
+ if (errno != EINTR)
+ return -errno;
+ } else {
+ sz -= (size_t)wsz;
+ if (sz == 0) {
+ rc = ftruncate(fb->backup, outo);
+ if (rc == 0)
+ rc = fsync(fb->backup);
+ return rc < 0 ? -errno : 0;
+ }
+ }
+ }
+}
+
+/** recover from latest backup */
+int
+fbuf_recover(
+ fbuf_t *fb
+) {
+ ssize_t ssz;
+ struct stat st;
+ int rc;
+
+ /* get the size */
+ rc = fstat(fb->backup, &st);
+ if (rc < 0)
+ return -errno;
+
+ /* ensure space */
+ if (st.st_size > UINT32_MAX)
+ return -EFBIG;
+ rc = fbuf_ensure_capacity(fb, (uint32_t)st.st_size);
+ if (rc < 0)
+ return rc;
+
+ /* read it */
+ ssz = pread(fb->backup, fb->buffer, (size_t)st.st_size, 0);
+ if (ssz < 0)
+ return -errno;
+
+ fb->used = (uint32_t)st.st_size;
+ fb->saved = fb->size = 0; /* ensure rewrite of restored data */
+ return 0;
+}
+
diff --git a/src/fbuf.h b/src/fbuf.h
index 3bbdbde..bf7d9c1 100644
--- a/src/fbuf.h
+++ b/src/fbuf.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#pragma once
/**
@@ -6,7 +23,7 @@
struct fbuf
{
/** filename for messages */
- const char *name;
+ char *name;
/** in memory copy of the file */
void *buffer;
@@ -25,18 +42,22 @@ struct fbuf
/** opened file descriptor for the file */
int fd;
+
+ /** opened file descriptor for the backup */
+ int backup;
};
/** short type */
typedef struct fbuf fbuf_t;
-/** open in 'fb' the file of 'name' */
+/** open in 'fb' the file of 'name' and optionnal 'backup' name */
extern
int
fbuf_open(
fbuf_t *fb,
- const char *name
+ const char *name,
+ const char *backup
);
/** close the file 'fb' */
@@ -105,7 +126,20 @@ int
fbuf_open_identify(
fbuf_t *fb,
const char *name,
+ const char *backup,
const char *id,
uint32_t idlen
);
+extern
+int
+fbuf_backup(
+ fbuf_t *fb
+);
+
+extern
+int
+fbuf_recover(
+ fbuf_t *fb
+);
+
diff --git a/src/lib-compat.c b/src/lib-compat.c
index 5c6c4e2..794487d 100644
--- a/src/lib-compat.c
+++ b/src/lib-compat.c
@@ -1,4 +1,21 @@
/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
cynara_admin_initialize(&m_CynaraAdmin),
cynara_admin_finish(m_CynaraAdmin);
cynara_admin_set_policies(m_CynaraAdmin, pp_policies.data()),
@@ -321,20 +338,26 @@ int cynara_admin_list_policies_descriptions(struct cynara_admin *p_cynara_admin,
}
/************************************* CLIENT-ASYNC **************************************/
+struct cynara_async_configuration { uint32_t szcache; };
int cynara_async_configuration_create(cynara_async_configuration **pp_conf)
{
- *pp_conf = (cynara_async_configuration*)pp_conf;
+ *pp_conf = malloc(sizeof(cynara_async_configuration));
+ if (*pp_conf == NULL)
+ return CYNARA_API_OUT_OF_MEMORY;
+ (*pp_conf)->szcache = 0;
return CYNARA_API_SUCCESS;
}
void cynara_async_configuration_destroy(cynara_async_configuration *p_conf)
{
+ free(p_conf);
}
int cynara_async_configuration_set_cache_size(cynara_async_configuration *p_conf,
size_t cache_size)
{
+ p_conf->szcache = cache_size > 1000000 ? 1000000 : (uint32_t)cache_size;
return CYNARA_API_SUCCESS;
}
@@ -385,7 +408,7 @@ int cynara_async_initialize(cynara_async **pp_cynara, const cynara_async_configu
if (p_cynara == NULL)
ret = CYNARA_API_OUT_OF_MEMORY;
else {
- ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, 0));
+ ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, p_conf ? p_conf->szcache : 0));
if (ret != CYNARA_API_SUCCESS)
free(p_cynara);
else {
@@ -437,7 +460,7 @@ static void reqcb(void *closure, int status)
*p = req->next;
if (!req->canceled)
- req->callback(req->id, CYNARA_CALL_CAUSE_ANSWER, status, req->user_response_data);
+ req->callback(req->id, CYNARA_CALL_CAUSE_ANSWER, from_check_status(status), req->user_response_data);
free(req);
}
@@ -506,24 +529,32 @@ int cynara_async_cancel_request(cynara_async *p_cynara, cynara_check_id check_id
/************************************* CLIENT **************************************/
+struct cynara_configuration { uint32_t szcache; };
+
int cynara_configuration_create(cynara_configuration **pp_conf)
{
- *pp_conf = (cynara_configuration*)pp_conf;
+ *pp_conf = malloc(sizeof(cynara_configuration));
+ if (*pp_conf == NULL)
+ return CYNARA_API_OUT_OF_MEMORY;
+ (*pp_conf)->szcache = 0;
return CYNARA_API_SUCCESS;
}
void cynara_configuration_destroy(cynara_configuration *p_conf)
{
+ free(p_conf);
}
-int cynara_configuration_set_cache_size(cynara_configuration *p_conf, size_t cache_size)
+int cynara_configuration_set_cache_size(cynara_configuration *p_conf,
+ size_t cache_size)
{
+ p_conf->szcache = cache_size > 1000000 ? 1000000 : (uint32_t)cache_size;
return CYNARA_API_SUCCESS;
}
int cynara_initialize(cynara **pp_cynara, const cynara_configuration *p_conf)
{
- return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, 0));
+ return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, p_conf ? p_conf->szcache : 0));
}
int cynara_finish(cynara *p_cynara)
diff --git a/src/main-cynarad.c b/src/main-cynarad.c
new file mode 100644
index 0000000..ba7d71c
--- /dev/null
+++ b/src/main-cynarad.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/capability.h>
+
+#if defined(WITH_SYSTEMD_ACTIVATION)
+#include <systemd/sd-daemon.h>
+#endif
+
+#include "db.h"
+#include "cyn.h"
+#include "rcyn-server.h"
+
+#if !defined(DEFAULT_DB_DIR)
+# define DEFAULT_DB_DIR "/var/lib/cynara"
+#endif
+#if !defined(DEFAULT_SOCKET_DIR)
+# define DEFAULT_SOCKET_DIR "/var/run/cynara"
+#endif
+#if !defined(DEFAULT_INIT_FILE)
+# define DEFAULT_INIT_FILE "/etc/security/cynara.initial"
+#endif
+#if !defined(DEFAULT_CYNARA_USER)
+# define DEFAULT_CYNARA_USER NULL
+#endif
+#if !defined(DEFAULT_CYNARA_GROUP)
+# define DEFAULT_CYNARA_GROUP NULL
+#endif
+
+#define _DBDIR_ 'd'
+#define _GROUP_ 'g'
+#define _HELP_ 'h'
+#define _INIT_ 'i'
+#define _MAKEDBDIR_ 'm'
+#define _MAKESOCKDIR_ 'M'
+#define _OWNSOCKDIR_ 'O'
+#define _OWNDBDIR_ 'o'
+#define _SOCKETDIR_ 'S'
+#define _SYSTEMD_ 's'
+#define _USER_ 'u'
+#define _VERSION_ 'v'
+
+static
+const char
+shortopts[] = "d:g:hi:mMOoS:u:v"
+#if defined(WITH_SYSTEMD_ACTIVATION)
+ "s"
+#endif
+;
+
+static
+const struct option
+longopts[] = {
+ { "dbdir", 1, NULL, _DBDIR_ },
+ { "group", 1, NULL, _GROUP_ },
+ { "help", 0, NULL, _HELP_ },
+ { "init", 1, NULL, _INIT_ },
+ { "make-db-dir", 0, NULL, _MAKEDBDIR_ },
+ { "make-socket-dir", 0, NULL, _MAKESOCKDIR_ },
+ { "own-db-dir", 0, NULL, _OWNDBDIR_ },
+ { "own-socket-dir", 0, NULL, _OWNSOCKDIR_ },
+ { "socketdir", 1, NULL, _SOCKETDIR_ },
+#if defined(WITH_SYSTEMD_ACTIVATION)
+ { "systemd", 0, NULL, _SYSTEMD_ },
+#endif
+ { "user", 1, NULL, _USER_ },
+ { "version", 0, NULL, _VERSION_ },
+ { NULL, 0, NULL, 0 }
+};
+
+static
+const char
+helptxt[] =
+ "\n"
+ "usage: cynarad [options]...\n"
+ "\n"
+ "otpions:\n"
+#if defined(WITH_SYSTEMD_ACTIVATION)
+ " -s, --systemd socket activation by systemd\n"
+#endif
+ " -u, --user xxx set the user\n"
+ " -g, --group xxx set the group\n"
+ " -i, --init xxx initialize if needed the database with content of file xxx\n"
+ "\n"
+ " -b, --dbdir xxx set the directory of database\n"
+ " (default: "DEFAULT_DB_DIR")\n"
+ " -m, --make-db-dir make the database directory\n"
+ " -o, --own-db-dir set user and group on database directory\n"
+ "\n"
+ " -S, --socketdir xxx set the base xxx for sockets\n"
+ " (default: "DEFAULT_SOCKET_DIR")\n"
+ " -M, --make-socket-dir make the socket directory\n"
+ " -O, --own-socket-dir set user and group on socket directory\n"
+ "\n"
+ " -h, --help print this help and exit\n"
+ " -v, --version print the version and exit\n"
+ "\n"
+;
+
+static
+const char
+versiontxt[] =
+ "cynarad version 1.99.99\n"
+;
+
+static int isid(const char *text);
+static void ensure_directory(const char *path, int uid, int gid);
+static void initdb(const char *path);
+int main(int ac, char **av)
+{
+ int opt;
+ int rc;
+ int makesockdir = 0;
+ int makedbdir = 0;
+ int owndbdir = 0;
+ int ownsockdir = 0;
+ int help = 0;
+ int version = 0;
+ int error = 0;
+ int systemd = 0;
+ int uid = -1;
+ int gid = -1;
+ const char *init = NULL;
+ const char *dbdir = NULL;
+ const char *socketdir = NULL;
+ const char *user = NULL;
+ const char *group = NULL;
+ struct passwd *pw;
+ struct group *gr;
+ cap_t caps = { 0 };
+ rcyn_server_t *server;
+ char *spec_socket_admin, *spec_socket_check;
+
+ /* scan arguments */
+ for (;;) {
+ opt = getopt_long(ac, av, shortopts, longopts, NULL);
+ if (opt == -1)
+ break;
+
+ switch(opt) {
+ case _DBDIR_:
+ dbdir = optarg;
+ break;
+ case _GROUP_:
+ group = optarg;
+ break;
+ case _HELP_:
+ help = 1;
+ break;
+ case _INIT_:
+ init = optarg;
+ break;
+ case _MAKEDBDIR_:
+ makedbdir = 1;
+ break;
+ case _MAKESOCKDIR_:
+ makesockdir = 1;
+ break;
+ case _OWNSOCKDIR_:
+ ownsockdir = 1;
+ break;
+ case _OWNDBDIR_:
+ owndbdir = 1;
+ break;
+ case _SOCKETDIR_:
+ socketdir = optarg;
+ break;
+#if defined(WITH_SYSTEMD_ACTIVATION)
+ case _SYSTEMD_:
+ systemd = 1;
+ break;
+#endif
+ case _USER_:
+ user = optarg;
+ break;
+ case _VERSION_:
+ version = 1;
+ break;
+ default:
+ error = 1;
+ break;
+ }
+ }
+
+ /* handles help, version, error */
+ if (help | version) {
+ fprintf(stdout, "%s", help ? helptxt : versiontxt);
+ return 0;
+ }
+ if (error)
+ return 1;
+ if (systemd && (socketdir || makesockdir)) {
+ fprintf(stderr, "can't set options --systemd and --%s together\n",
+ socketdir ? "socketdir" : "make-socket-dir");
+ return 1;
+ }
+
+ /* set the defaults */
+ dbdir = dbdir ?: DEFAULT_DB_DIR;
+ socketdir = socketdir ?: DEFAULT_SOCKET_DIR;
+ user = user ?: DEFAULT_CYNARA_USER;
+ group = group ?: DEFAULT_CYNARA_GROUP;
+ init = init ?: DEFAULT_INIT_FILE;
+
+ /* compute socket specs */
+ spec_socket_admin = spec_socket_check = NULL;
+ if (systemd) {
+ spec_socket_admin = strdup("sd:admin");
+ spec_socket_check = strdup("sd:check");
+ } else {
+ rc = asprintf(&spec_socket_admin, "unix:%s/cynara.admin", socketdir);
+ rc = asprintf(&spec_socket_check, "unix:%s/cynara.check", socketdir);
+ }
+ if (spec_socket_admin == NULL || spec_socket_check == NULL) {
+ fprintf(stderr, "can't make socket paths\n");
+ return 1;
+ }
+
+ /* compute user and group */
+ if (user) {
+ uid = isid(user);
+ if (uid < 0) {
+ pw = getpwnam(user);
+ if (pw == NULL) {
+ fprintf(stderr, "can not find user '%s'\n", user);
+ return -1;
+ }
+ uid = pw->pw_uid;
+ gid = pw->pw_gid;
+ }
+ }
+ if (group) {
+ gid = isid(group);
+ if (gid < 0) {
+ gr = getgrnam(group);
+ if (gr == NULL) {
+ fprintf(stderr, "can not find group '%s'\n", group);
+ return -1;
+ }
+ gid = gr->gr_gid;
+ }
+ }
+
+ /* handle directories */
+ if (makedbdir)
+ ensure_directory(dbdir, owndbdir ? uid : -1, owndbdir ? gid : -1);
+ if (makesockdir && socketdir[0] != '@')
+ ensure_directory(socketdir, ownsockdir ? uid : -1, ownsockdir ? gid : -1);
+
+ /* drop privileges */
+ if (gid >= 0) {
+ rc = setgid(gid);
+ if (rc < 0) {
+ fprintf(stderr, "can not change group: %m\n");
+ return -1;
+ }
+ }
+ if (uid >= 0) {
+ rc = setuid(uid);
+ if (rc < 0) {
+ fprintf(stderr, "can not change user: %m\n");
+ return -1;
+ }
+ }
+ cap_clear(caps);
+ rc = cap_set_proc(caps);
+
+ /* connection to the database */
+ rc = db_open(dbdir);
+ if (rc < 0) {
+ fprintf(stderr, "can not open database: %m\n");
+ return 1;
+ }
+
+ /* initialisation of the database */
+ if (db_is_empty()) {
+ initdb(init);
+ if (rc == 0)
+ rc = db_sync();
+ if (rc == 0)
+ rc = db_backup();
+ if (rc < 0) {
+ fprintf(stderr, "can't initialise database: %m\n");
+ return 1;
+ }
+ }
+
+ /* initialize server */
+ signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */
+ rc = rcyn_server_create(&server, spec_socket_admin, spec_socket_check);
+ if (rc < 0) {
+ fprintf(stderr, "can't initialise server: %m\n");
+ return 1;
+ }
+
+ /* ready ! */
+#if defined(WITH_SYSTEMD_ACTIVATION)
+ if (systemd)
+ sd_notify(0, "READY=1");
+#endif
+
+ /* serve */
+ rc = rcyn_server_serve(server);
+ return rc ? 3 : 0;
+}
+
+/** returns the value of the id for 'text' (positive) or a negative value (-1) */
+static int isid(const char *text)
+{
+ long long int value = 0;
+ while(*text && value < INT_MAX)
+ if (*text < '0' || *text > '9' || value >= INT_MAX)
+ return -1;
+ else
+ value = 10 * value + (*text++ - '0');
+ return value <= INT_MAX ? (int)value : -1;
+}
+
+/** returns a pointer to the first last / of the path if it is meaningful */
+static char *enddir(char *path)
+{
+ /*
+ * / -> NULL
+ * /xxx -> NULL
+ * /xxx/ -> NULL
+ * /xxx/y -> /y
+ * /xxx//y -> //y
+ */
+ char *c = NULL, *r = NULL, *i = path;
+ for(;;) {
+ while(*i == '/')
+ i++;
+ if (*i)
+ r = c;
+ while(*i != '/')
+ if (!*i++)
+ return r;
+ c = i;
+ }
+}
+
+/** ensure that 'path' is a directory for the user and group */
+static void ensuredir(char *path, int length, int uid, int gid)
+{
+ struct stat st;
+ int rc, n;
+ char *e;
+
+ n = length;
+ for(;;) {
+ path[n] = 0;
+ rc = mkdir(path, 0755);
+ if (rc == 0 || errno == EEXIST) {
+ /* exists */
+ if (n == length) {
+ rc = stat(path, &st);
+ if (rc < 0) {
+ fprintf(stderr, "can not check %s: %m\n", path);
+ exit(1);
+ } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
+ fprintf(stderr, "not a directory %s: %m\n", path);
+ exit(1);
+ }
+ /* set ownership */
+ if ((uid != st.st_uid && uid >= 0) || (gid != st.st_gid && gid >= 0)) {
+ rc = chown(path, uid, gid);
+ if (rc < 0) {
+ fprintf(stderr, "can not own directory %s for uid=%d & gid=%d: %m\n", path, uid, gid);
+ exit(1);
+ }
+ }
+ return;
+ }
+ path[n] = '/';
+ n = (int)strlen(path);
+ } else if (errno == ENOENT) {
+ /* a part of the path doesn't exist, try to create it */
+ e = enddir(path);
+ if (!e) {
+ /* can't create it because at root */
+ fprintf(stderr, "can not ensure directory %s\n", path);
+ exit(1);
+ }
+ n = (int)(e - path);
+ } else {
+ fprintf(stderr, "can not ensure directory %s: %m\n", path);
+ exit(1);
+ }
+ }
+}
+
+/** ensure that 'path' is a directory for the user and group */
+static void ensure_directory(const char *path, int uid, int gid)
+{
+ size_t l;
+ char *p;
+
+ l = strlen(path);
+ if (l > INT_MAX) {
+ /* ?!?!?!? *#@! */
+ fprintf(stderr, "path toooooo long\n");
+ exit(1);
+ }
+ p = strndupa(path, l);
+ ensuredir(p, (int)l, uid, gid);
+}
+
+/** initialize the database from file of 'path' */
+static void initdb(const char *path)
+{
+ int rc, lino, x;
+ char *item[10];
+ char buffer[2048];
+ FILE *f;
+
+ f = fopen(path, "r");
+ if (f == NULL) {
+ fprintf(stderr, "can't open file %s\n", path);
+ exit(1);
+ }
+
+ lino = 0;
+ while(fgets(buffer, sizeof buffer, f)) {
+ lino++;
+ item[0] = strtok(buffer, " \t\n\r");
+ if (item[0] && item[0][0] != '#') {
+ item[1] = strtok(NULL, " \t\n\r");
+ item[2] = strtok(NULL, " \t\n\r");
+ item[3] = strtok(NULL, " \t\n\r");
+ item[4] = strtok(NULL, " \t\n\r");
+ item[5] = strtok(NULL, " \t\n\r");
+ if (item[1] == NULL || item[2] == NULL
+ || item[3] == NULL || item[4] == NULL) {
+ fprintf(stderr, "field missing (%s:%d)\n", path, lino);
+ exit(1);
+ } else if (item[5] != NULL && item[5][0] != '#') {
+ fprintf(stderr, "extra field (%s:%d)\n", path, lino);
+ exit(1);
+ }
+ x = isid(item[4]);
+ if (x < 0) {
+ fprintf(stderr, "bad value (%s:%d)\n", path, lino);
+ exit(1);
+ }
+ rc = db_set(item[0], item[1], item[2], item[3], x);
+ if (rc < 0) {
+ fprintf(stderr, "can't set (%s:%d)\n", path, lino);
+ exit(1);
+ }
+ }
+ }
+ if (!feof(f)) {
+ fprintf(stderr, "error while reading file %s\n", path);
+ exit(1);
+ }
+ fclose(f);
+}
+
diff --git a/src/prot.c b/src/prot.c
index db0e41e..bb58acf 100644
--- a/src/prot.c
+++ b/src/prot.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#define _GNU_SOURCE
#include <stdlib.h>
diff --git a/src/prot.h b/src/prot.h
index a1ded3b..b9f83ea 100644
--- a/src/prot.h
+++ b/src/prot.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#pragma once
struct prot;
diff --git a/src/queue.c b/src/queue.c
index 16b0a0f..e2fb84c 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
diff --git a/src/queue.h b/src/queue.h
index 70518c4..1937ee1 100644
--- a/src/queue.h
+++ b/src/queue.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
extern
int
diff --git a/src/rcyn-client.c b/src/rcyn-client.c
index f712ca6..e1f7841 100644
--- a/src/rcyn-client.c
+++ b/src/rcyn-client.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#define _GNU_SOURCE
#include <stdbool.h>
@@ -106,7 +123,7 @@ flushw(
if (rc == -EAGAIN) {
pfd.fd = rcyn->fd;
pfd.events = POLLOUT;
- do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR);
+ do { rc = poll(&pfd, 1, -1); } while (rc < 0 && errno == EINTR);
if (rc < 0)
rc = -errno;
}
@@ -170,7 +187,7 @@ wait_input(
pfd.fd = rcyn->fd;
pfd.events = POLLIN;
- do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR);
+ do { rc = poll(&pfd, 1, -1); } while (rc < 0 && errno == EINTR);
return rc < 0 ? -errno : 0;
}
@@ -284,7 +301,7 @@ async(
int op,
uint32_t events
) {
- return rcyn->async.controlcb
+ return rcyn->async.controlcb && rcyn->fd >= 0
? rcyn->async.controlcb(rcyn->async.closure, op, rcyn->fd, events)
: 0;
}
@@ -344,6 +361,13 @@ connection(
return rc;
}
+static
+int
+ensure_opened(
+ rcyn_t *rcyn
+) {
+ return rcyn->fd < 0 ? connection(rcyn) : 0;
+}
/************************************************************************************/
@@ -375,17 +399,12 @@ rcyn_open(
rcyn->async.closure = 0;
rcyn->async.requests = NULL;
- /* connection */
- rc = connection(rcyn);
- if (rc < 0)
- goto error3;
+ /* lazy connection */
+ rcyn->fd = -1;
/* done */
return 0;
-error3:
- free(rcyn->cache);
- prot_destroy(rcyn->prot);
error2:
free(rcyn);
error:
@@ -414,6 +433,9 @@ rcyn_enter(
return -EPERM;
if (rcyn->async.requests != NULL)
return -EINPROGRESS;
+ rc = ensure_opened(rcyn);
+ if (rc < 0)
+ return rc;
rc = putx(rcyn, _enter_, NULL);
if (rc >= 0)
@@ -432,6 +454,9 @@ rcyn_leave(
return -EPERM;
if (rcyn->async.requests != NULL)
return -EINPROGRESS;
+ rc = ensure_opened(rcyn);
+ if (rc < 0)
+ return rc;
rc = putx(rcyn, _leave_, commit ? _commit_ : NULL/*default: rollback*/, NULL);
if (rc >= 0)
@@ -453,6 +478,9 @@ check_or_test(
if (rcyn->async.requests != NULL)
return -EINPROGRESS;
+ rc = ensure_opened(rcyn);
+ if (rc < 0)
+ return rc;
/* ensure there is no clear cache pending */
flushr(rcyn);
@@ -514,6 +542,9 @@ rcyn_set(
return -EPERM;
if (rcyn->async.requests != NULL)
return -EINPROGRESS;
+ rc = ensure_opened(rcyn);
+ if (rc < 0)
+ return rc;
snprintf(val, sizeof val, "%u", (unsigned)value);
rc = putx(rcyn, _set_, client, session, user, permission, val, NULL);
@@ -545,6 +576,9 @@ rcyn_get(
return -EPERM;
if (rcyn->async.requests != NULL)
return -EINPROGRESS;
+ rc = ensure_opened(rcyn);
+ if (rc < 0)
+ return rc;
rc = putx(rcyn, _get_, client, session, user, permission, NULL);
if (rc >= 0) {
@@ -577,6 +611,9 @@ rcyn_drop(
return -EPERM;
if (rcyn->async.requests != NULL)
return -EINPROGRESS;
+ rc = ensure_opened(rcyn);
+ if (rc < 0)
+ return rc;
rc = putx(rcyn, _drop_, client, session, user, permission, NULL);
if (rc >= 0)
@@ -645,6 +682,7 @@ rcyn_async_process(
int rc;
const char *first;
asreq_t *ar;
+ const char *client, *session, *user, *permission;
for (;;) {
/* non blocking wait for a reply */
@@ -670,6 +708,13 @@ rcyn_async_process(
/* emit the asynchronous answer */
rcyn->async.requests = ar->next;
rc = status_check(rcyn);
+ if (rc >= 0) {
+ client = (const char*)(ar + 1);
+ session = &client[1 + strlen(client)];
+ user = &session[1 + strlen(session)];
+ permission = &user[1 + strlen(user)];
+ cache_put(rcyn->cache, client, session, user, permission, rc);
+ }
ar->callback(ar->closure, rc);
free(ar);
}
@@ -691,8 +736,12 @@ rcyn_async_check(
int rc;
asreq_t **pr, *ar;
+ rc = ensure_opened(rcyn);
+ if (rc < 0)
+ return rc;
+
/* allocate */
- ar = malloc(sizeof *ar);
+ ar = malloc(sizeof *ar + strlen(client) + strlen(session) + strlen(user) + strlen(permission) + 4);
if (ar == NULL)
return -ENOMEM;
@@ -700,6 +749,7 @@ rcyn_async_check(
ar->next = NULL;
ar->callback = callback;
ar->closure = closure;
+ stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)(ar + 1), client), session), user), permission);
/* send the request */
rc = putx(rcyn, simple ? _test_ : _check_,
diff --git a/src/rcyn-client.h b/src/rcyn-client.h
index 6706820..a4104ee 100644
--- a/src/rcyn-client.h
+++ b/src/rcyn-client.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#pragma once
diff --git a/src/rcyn-protocol.c b/src/rcyn-protocol.c
index c656570..868e81f 100644
--- a/src/rcyn-protocol.c
+++ b/src/rcyn-protocol.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include "rcyn-protocol.h"
const char
diff --git a/src/rcyn-protocol.h b/src/rcyn-protocol.h
index 8906194..631c0b7 100644
--- a/src/rcyn-protocol.h
+++ b/src/rcyn-protocol.h
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#pragma once
extern const char
diff --git a/src/rcyn-server.c b/src/rcyn-server.c
index 4fc365e..2fff5db 100644
--- a/src/rcyn-server.c
+++ b/src/rcyn-server.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#define _GNU_SOURCE
#include <stdbool.h>
@@ -11,17 +28,15 @@
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
+#include <limits.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
-#if defined(WITH_SYSTEMD_ACTIVATION)
-#include <systemd/sd-daemon.h>
-#endif
-
#include "prot.h"
#include "cyn.h"
#include "rcyn-protocol.h"
+#include "rcyn-server.h"
#include "socket.h"
typedef enum rcyn_type {
@@ -29,6 +44,7 @@ typedef enum rcyn_type {
rcyn_Admin
} rcyn_type_t;
+
/** structure for using epoll easily */
typedef struct pollitem pollitem_t;
struct pollitem
@@ -37,10 +53,10 @@ struct pollitem
void (*handler)(pollitem_t *pollitem, uint32_t events, int pollfd);
/** data */
- union {
- void *closure;
- int fd;
- };
+ void *closure;
+
+ /** file */
+ int fd;
};
static
@@ -48,12 +64,11 @@ int
pollitem_do(
pollitem_t *pollitem,
uint32_t events,
- int fd,
int pollfd,
int op
) {
struct epoll_event ev = { .events = events, .data.ptr = pollitem };
- return epoll_ctl(pollfd, op, fd, &ev);
+ return epoll_ctl(pollfd, op, pollitem->fd, &ev);
}
static
@@ -61,10 +76,9 @@ int
pollitem_add(
pollitem_t *pollitem,
uint32_t events,
- int fd,
int pollfd
) {
- return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_ADD);
+ return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_ADD);
}
#if 0
@@ -73,20 +87,19 @@ int
pollitem_mod(
pollitem_t *pollitem,
uint32_t events,
- int fd,
int pollfd
) {
- return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_MOD);
+ return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_MOD);
}
#endif
static
int
pollitem_del(
- int fd,
+ pollitem_t *pollitem,
int pollfd
) {
- return pollitem_do(NULL, 0, fd, pollfd, EPOLL_CTL_DEL);
+ return pollitem_do(pollitem, 0, pollfd, EPOLL_CTL_DEL);
}
@@ -96,9 +109,6 @@ struct client
/** a protocol structure */
prot_t *prot;
- /** the in/out file descriptor */
- int fd;
-
/** type of client */
rcyn_type_t type;
@@ -125,6 +135,22 @@ struct client
};
typedef struct client client_t;
+/** structure for servers */
+struct rcyn_server
+{
+ /** the pollfd to use */
+ int pollfd;
+
+ /** is stopped ? */
+ int stopped;
+
+ /** the admin socket */
+ pollitem_t admin;
+
+ /** the check socket */
+ pollitem_t check;
+};
+
/**
* Check 'arg' against 'value' beginning at offset accepting it if 'arg' prefixes 'value'
* Returns 1 if matching or 0 if not.
@@ -157,9 +183,9 @@ flushw(
rc = prot_should_write(cli->prot);
while (rc) {
- rc = prot_write(cli->prot, cli->fd);
+ rc = prot_write(cli->prot, cli->pollitem.fd);
if (rc == -EAGAIN) {
- pfd.fd = cli->fd;
+ pfd.fd = cli->pollitem.fd;
pfd.events = POLLOUT;
do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR);
}
@@ -418,7 +444,7 @@ destroy_client(
bool closefds
) {
if (closefds)
- close(cli->fd);
+ close(cli->pollitem.fd);
if (cli->entering)
cyn_enter_async_cancel(entercb, cli);
if (cli->entered)
@@ -446,7 +472,7 @@ on_client_event(
/* possible input */
if (events & EPOLLIN) {
- nr = prot_read(cli->prot, cli->fd);
+ nr = prot_read(cli->prot, cli->pollitem.fd);
if (nr <= 0)
goto terminate;
nargs = prot_get(cli->prot, &args);
@@ -462,7 +488,7 @@ on_client_event(
/* terminate the client session */
terminate:
- pollitem_del(cli->fd, pollfd);
+ pollitem_del(&cli->pollitem, pollfd);
destroy_client(cli, true);
}
@@ -495,7 +521,6 @@ create_client(
goto error3;
/* records the file descriptor */
- cli->fd = fd;
cli->type = type;
cli->version = 0; /* version not set */
cli->relax = true; /* relax on error */
@@ -505,6 +530,7 @@ create_client(
cli->checked = false; /* no check made */
cli->pollitem.handler = on_client_event;
cli->pollitem.closure = cli;
+ cli->pollitem.fd = fd;
return 0;
error3:
prot_destroy(cli->prot);
@@ -559,7 +585,7 @@ on_server_event(
}
/* add the client to the epolling */
- rc = pollitem_add(&cli->pollitem, EPOLLIN, fd, pollfd);
+ rc = pollitem_add(&cli->pollitem, EPOLLIN, pollfd);
if (rc < 0) {
fprintf(stderr, "can't poll client connection: %s\n", strerror(-rc));
destroy_client(cli, 1);
@@ -589,66 +615,159 @@ on_admin_server_event(
on_server_event(pollitem, events, pollfd, rcyn_Admin);
}
-int main(int ac, char **av)
-{
- int rc;
- pollitem_t pi_admin, pi_check, *pi;
- int pollfd, fd;
- struct epoll_event ev;
- const char *check_spec = rcyn_default_check_socket_spec;
- const char *admin_spec = rcyn_default_admin_socket_spec;
+/** destroy a server */
+void
+rcyn_server_destroy(
+ rcyn_server_t *server
+) {
+ if (server) {
+ if (server->pollfd >= 0)
+ close(server->pollfd);
+ if (server->admin.fd >= 0)
+ close(server->admin.fd);
+ if (server->check.fd >= 0)
+ close(server->check.fd);
+ free(server);
+ }
+}
- /*
- * future possible options:
- * - strict/relax
- * - databse name(s)
- * - socket name
- * - policy
- */
+/** create a server */
+int
+rcyn_server_create(
+ rcyn_server_t **server,
+ const char *admin_socket_spec,
+ const char *check_socket_spec
+) {
+ rcyn_server_t *srv;
+ int rc;
- /* connection to the database */
- rc = cyn_init();
- if (rc < 0) {
- fprintf(stderr, "can't initialise database: %m\n");
- return 1;
+ /* allocate the structure */
+ *server = srv = malloc(sizeof *srv);
+ if (srv == NULL) {
+ rc = -ENOMEM;
+ fprintf(stderr, "can't alloc memory: %m\n");
+ goto error;
}
/* create the polling fd */
- pollfd = epoll_create1(EPOLL_CLOEXEC);
- if (pollfd < 0) {
+ srv->admin.fd = srv->check.fd = -1;
+ srv->pollfd = epoll_create1(EPOLL_CLOEXEC);
+ if (srv->pollfd < 0) {
+ rc = -errno;
fprintf(stderr, "can't create polling: %m\n");
- return 1;
+ goto error2;
}
/* create the admin server socket */
- fd = socket_open(admin_spec, 1);
- if (fd < 0) {
- fprintf(stderr, "can't create admin server socket: %m\n");
- return 1;
+ admin_socket_spec = admin_socket_spec ?: rcyn_default_admin_socket_spec;
+ srv->admin.fd = socket_open(admin_socket_spec, 1);
+ if (srv->admin.fd < 0) {
+ rc = -errno;
+ fprintf(stderr, "can't create admin server socket %s: %m\n", admin_socket_spec);
+ goto error2;
}
/* add the server to pollfd */
- pi_admin.handler = on_admin_server_event;
- pi_admin.fd = fd;
- rc = pollitem_add(&pi_admin, EPOLLIN, fd, pollfd);
+ srv->admin.handler = on_admin_server_event;
+ srv->admin.closure = srv;
+ rc = pollitem_add(&srv->admin, EPOLLIN, srv->pollfd);
if (rc < 0) {
+ rc = -errno;
fprintf(stderr, "can't poll admin server: %m\n");
- return 1;
+ goto error2;
}
/* create the server socket */
- fd = socket_open(check_spec, 1);
- if (fd < 0) {
- fprintf(stderr, "can't create check server socket: %m\n");
- return 1;
+ check_socket_spec = check_socket_spec ?: rcyn_default_check_socket_spec;
+ srv->check.fd = socket_open(check_socket_spec, 1);
+ if (srv->check.fd < 0) {
+ rc = -errno;
+ fprintf(stderr, "can't create check server socket %s: %m\n", check_socket_spec);
+ goto error2;
}
/* add the server to pollfd */
- pi_check.handler = on_check_server_event;
- pi_check.fd = fd;
- rc = pollitem_add(&pi_check, EPOLLIN, fd, pollfd);
+ srv->check.handler = on_check_server_event;
+ srv->check.closure = srv;
+ rc = pollitem_add(&srv->check, EPOLLIN, srv->pollfd);
if (rc < 0) {
+ rc = -errno;
fprintf(stderr, "can't poll check server: %m\n");
+ goto error2;
+ }
+ return 0;
+
+error2:
+ if (srv->pollfd >= 0)
+ close(srv->pollfd);
+ if (srv->admin.fd >= 0)
+ close(srv->admin.fd);
+ if (srv->check.fd >= 0)
+ close(srv->check.fd);
+ free(srv);
+error:
+ *server = NULL;
+ return rc;
+}
+
+/** stop the server */
+void
+rcyn_server_stop(
+ rcyn_server_t *server,
+ int status
+) {
+ server->stopped = status ?: INT_MIN;
+}
+
+/** create a server */
+int
+rcyn_server_serve(
+ rcyn_server_t *server
+) {
+ int rc;
+ struct epoll_event ev;
+ pollitem_t *pi;
+
+ /* process inputs */
+ server->stopped = 0;
+ while(!server->stopped) {
+ rc = epoll_wait(server->pollfd, &ev, 1, -1);
+ if (rc == 1) {
+ pi = ev.data.ptr;
+ pi->handler(pi, ev.events, server->pollfd);
+ }
+ }
+ return server->stopped == INT_MIN ? 0 : server->stopped;
+}
+
+#if 0
+#if defined(WITH_SYSTEMD_ACTIVATION)
+#include <systemd/sd-daemon.h>
+#endif
+
+
+int main(int ac, char **av)
+{
+ int rc;
+ const char *check_spec = rcyn_default_check_socket_spec;
+ const char *admin_spec = rcyn_default_admin_socket_spec;
+ rcyn_server_t *server;
+
+ /* connection to the database */
+ rc = cyn_init(
+ "/var/lib/cynara/cynara.names",
+ "/var/lib/cynara/cynara.rules",
+ (const char**)((const char *[]){ "System", "*", "*", "*", "1", NULL })
+ );
+ if (rc < 0) {
+ fprintf(stderr, "can't initialise database: %m\n");
+ return 1;
+ }
+
+ /* create the server */
+ rc = rcyn_server_create(&server, admin_spec, check_spec);
+ if (rc < 0) {
+ fprintf(stderr, "can't initialise server: %m\n");
return 1;
}
@@ -658,12 +777,7 @@ int main(int ac, char **av)
#endif
/* process inputs */
- for(;;) {
- rc = epoll_wait(pollfd, &ev, 1, -1);
- if (rc == 1) {
- pi = ev.data.ptr;
- pi->handler(pi, ev.events, pollfd);
- }
- }
+ rcyn_server_serve(server);
}
+#endif
diff --git a/src/rcyn-server.h b/src/rcyn-server.h
new file mode 100644
index 0000000..2908c52
--- /dev/null
+++ b/src/rcyn-server.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#pragma once
+
+struct rcyn_server;
+typedef struct rcyn_server rcyn_server_t;
+
+extern
+void
+rcyn_server_destroy(
+ rcyn_server_t *server
+);
+
+extern
+int
+rcyn_server_create(
+ rcyn_server_t **server,
+ const char *admin_socket_spec,
+ const char *check_socket_spec
+);
+
+extern
+int
+rcyn_server_serve(
+ rcyn_server_t *server
+);
+
+extern
+void
+rcyn_server_stop(
+ rcyn_server_t *server,
+ int status
+);
+
diff --git a/src/socket.c b/src/socket.c
index 3ab0408..b908cf0 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2018 "IoT.bzh"
+ * Copyright (C) 2018 "IoT.bzh"
* Author José Bollo <jose.bollo@iot.bzh>
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/test-lib-compat.c b/src/test-lib-compat.c
index 627c208..9f9ea1b 100644
--- a/src/test-lib-compat.c
+++ b/src/test-lib-compat.c
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
@@ -55,7 +72,7 @@ void ckex(int rc, int type, int line, const char *x)
printf("ERROR(%d) %s by %s line %d\n", rc, buffer, x, line);
exit(1);
}
- printf("SUCCESS %s\n", x);
+ printf("SUCCESS[%d] %s\n", rc, x);
}
int is(const char *first, const char *second, int mincount)
diff --git a/systemd/CMakeLists.txt b/systemd/CMakeLists.txt
index f8116a2..3fde22f 100644
--- a/systemd/CMakeLists.txt
+++ b/systemd/CMakeLists.txt
@@ -1,25 +1,23 @@
-# Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved
+###########################################################################
+# Copyright (C) 2018 "IoT.bzh"
#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
+# author: José Bollo <jose.bollo@iot.bzh>
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# @file CMakeLists.txt
-# @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+# http://www.apache.org/licenses/LICENSE-2.0
#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
-SET(CYNARA_ADMIN_SOCKET_GROUP
- "security_fw"
- CACHE STRING
- "Group to apply on administrative sockets")
+set(SYSTEMD_UNIT_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/systemd/system"
+ CACHE PATH "Path to systemd system unit files")
CONFIGURE_FILE(cynara-admin.socket.in cynara-admin.socket @ONLY)
CONFIGURE_FILE(cynara-check.socket.in cynara-check.socket @ONLY)
diff --git a/systemd/cynara-admin.socket.in b/systemd/cynara-admin.socket.in
index ebc59c6..25c3b2c 100644
--- a/systemd/cynara-admin.socket.in
+++ b/systemd/cynara-admin.socket.in
@@ -1,6 +1,6 @@
[Socket]
FileDescriptorName=admin
-ListenStream=@SOCKET_DIR@/cynara.admin
+ListenStream=@DEFAULT_SOCKET_DIR@/cynara.admin
SocketMode=0600
SmackLabelIPIn=@
SmackLabelIPOut=@
diff --git a/systemd/cynara-check.socket.in b/systemd/cynara-check.socket.in
index 1139d2f..b0606e0 100644
--- a/systemd/cynara-check.socket.in
+++ b/systemd/cynara-check.socket.in
@@ -1,6 +1,6 @@
[Socket]
FileDescriptorName=check
-ListenStream=@SOCKET_DIR@/cynara.check
+ListenStream=@DEFAULT_SOCKET_DIR@/cynara.check
SocketMode=0666
SmackLabelIPIn=*
SmackLabelIPOut=@
diff --git a/systemd/cynara.service b/systemd/cynara.service
index e124b91..9cacce6 100644
--- a/systemd/cynara.service
+++ b/systemd/cynara.service
@@ -4,8 +4,7 @@ Requires=afm-system-setup.service
After=afm-system-setup.service
[Service]
-ExecStartPre=+-/usr/bin/sh -c 'if test ! -d /var/lib/cynara; then mkdir -p /var/lib/cynara; chown cynara:cynara /var/lib/cynara; chsmack -a System /var/lib/cynara; fi'
-ExecStart=/usr/bin/cynarad
+ExecStart=/usr/bin/cynarad --systemd --user cynara --group cynara --make-db-dir --own-db-dir
Type=notify
@@ -15,11 +14,11 @@ Restart=always
Sockets=cynara-admin.socket
Sockets=cynara-check.socket
-
-UMask=0000
-User=cynara
-Group=cynara
SmackProcessLabel=System
+
+#UMask=0000
+#User=cynara
+#Group=cynara
#NoNewPrivileges=true
#Environment="CYNARA_LOG_LEVEL=LOG_DEBUG"