Make it work for AGL
authorJosé Bollo <jose.bollo@iot.bzh>
Tue, 11 Sep 2018 09:28:34 +0000 (11:28 +0200)
committerJose Bollo <jose.bollo@iot.bzh>
Mon, 17 Sep 2018 11:17:56 +0000 (13:17 +0200)
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
29 files changed:
CMakeLists.txt
cynara.initial [new file with mode: 0644]
src/CMakeLists.txt
src/cache.c
src/cache.h
src/cyn.c
src/cyn.h
src/db.c
src/db.h
src/fbuf.c
src/fbuf.h
src/lib-compat.c
src/main-cynarad.c [new file with mode: 0644]
src/prot.c
src/prot.h
src/queue.c
src/queue.h
src/rcyn-client.c
src/rcyn-client.h
src/rcyn-protocol.c
src/rcyn-protocol.h
src/rcyn-server.c
src/rcyn-server.h [new file with mode: 0644]
src/socket.c
src/test-lib-compat.c
systemd/CMakeLists.txt
systemd/cynara-admin.socket.in
systemd/cynara-check.socket.in
systemd/cynara.service

index 8a3c9f1..d4ed7c5 100644 (file)
@@ -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 (file)
index 0000000..96054d3
--- /dev/null
@@ -0,0 +1,4 @@
+# initial database for cynara
+System * * * 1
+User * * * 1
+
index fa3724c..4363234 100644 (file)
@@ -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()
 
index c2db00d..8d15728 100644 (file)
@@ -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;
 }
 
index 2f3a9a1..5cd827b 100644 (file)
@@ -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;
index 4eb6d9e..85deb4e 100644 (file)
--- 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),
index 0a061c2..df08a5a 100644 (file)
--- 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
index c7efdca..f5f46da 100644 (file)
--- 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(
index ce56e8c..0639833 100644 (file)
--- 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
index 6d4d41c..c1d4475 100644 (file)
@@ -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;
+}
+
index 3bbdbde..bf7d9c1 100644 (file)
@@ -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
+);
+
index 5c6c4e2..794487d 100644 (file)
@@ -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.
+ */
+
 /*
 cynara_admin_initialize(&m_CynaraAdmin),
 cynara_admin_finish(m_CynaraAdmin);
@@ -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 (file)
index 0000000..ba7d71c
--- /dev/null
@@ -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);
+}
+
index db0e41e..bb58acf 100644 (file)
@@ -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>
index a1ded3b..b9f83ea 100644 (file)
@@ -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;
index 16b0a0f..e2fb84c 100644 (file)
@@ -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>
index 70518c4..1937ee1 100644 (file)
@@ -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
index f712ca6..e1f7841 100644 (file)
@@ -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_,
index 6706820..a4104ee 100644 (file)
@@ -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
 
index c656570..868e81f 100644 (file)
@@ -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
index 8906194..631c0b7 100644 (file)
@@ -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
index 4fc365e..2fff5db 100644 (file)
@@ -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>
 #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 (file)
index 0000000..2908c52
--- /dev/null
@@ -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
+);
+
index 3ab0408..b908cf0 100644 (file)
@@ -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");
index 627c208..9f9ea1b 100644 (file)
@@ -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)
index f8116a2..3fde22f 100644 (file)
@@ -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)
index ebc59c6..25c3b2c 100644 (file)
@@ -1,6 +1,6 @@
 [Socket]
 FileDescriptorName=admin
-ListenStream=@SOCKET_DIR@/cynara.admin
+ListenStream=@DEFAULT_SOCKET_DIR@/cynara.admin
 SocketMode=0600
 SmackLabelIPIn=@
 SmackLabelIPOut=@
index 1139d2f..b0606e0 100644 (file)
@@ -1,6 +1,6 @@
 [Socket]
 FileDescriptorName=check
-ListenStream=@SOCKET_DIR@/cynara.check
+ListenStream=@DEFAULT_SOCKET_DIR@/cynara.check
 SocketMode=0666
 SmackLabelIPIn=*
 SmackLabelIPOut=@
index e124b91..9cacce6 100644 (file)
@@ -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"