summaryrefslogtreecommitdiffstats
path: root/src/afm-udb.c
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2017-03-16 09:36:13 +0100
committerJosé Bollo <jose.bollo@iot.bzh>2017-03-17 13:01:40 +0100
commit550ca0ae2bb9138c85fda941b67d4db1da1609ed (patch)
tree4c603caedb8c0bfc1fe174f9dd04809bed8a5467 /src/afm-udb.c
parent600a5cde310c62e4c6ed1845cf0234ba04fb6f90 (diff)
Switch to use systemd database
Installation now creates unit files. This commits use these created unit files to fill the application database of afm-user-daemon. Change-Id: Ice39d3ff51b6afe41609f3ce4ff0e89b2f3a0ca7 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
Diffstat (limited to 'src/afm-udb.c')
-rw-r--r--src/afm-udb.c444
1 files changed, 444 insertions, 0 deletions
diff --git a/src/afm-udb.c b/src/afm-udb.c
new file mode 100644
index 0000000..9b1a354
--- /dev/null
+++ b/src/afm-udb.c
@@ -0,0 +1,444 @@
+/*
+ Copyright 2015, 2016, 2017 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 <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <json-c/json.h>
+
+#include "utils-json.h"
+#include "utils-systemd.h"
+#include "utils-file.h"
+
+#include "afm-udb.h"
+
+
+static const char x_afm_prefix[] = "X-AFM-";
+static const char service_extension[] = ".service";
+static const char key_unit_path[] = "-unit-path";
+static const char key_unit_name[] = "-unit-name";
+static const char key_unit_scope[] = "-unit-scope";
+static const char scope_user[] = "user";
+static const char scope_system[] = "system";
+static const char key_id[] = "id";
+
+#define x_afm_prefix_length (sizeof x_afm_prefix - 1)
+#define service_extension_length (sizeof service_extension - 1)
+
+/*
+ * The structure afm_apps records the data about applications
+ * for several accesses.
+ */
+struct afm_apps {
+ struct json_object *prvarr; /* array of the private data of apps */
+ struct json_object *pubarr; /* array of the public data of apps */
+ struct json_object *pubobj; /* hash of application's publics */
+ struct json_object *prvobj; /* hash of application's privates */
+};
+
+/*
+ * The structure afm_udb records the applications
+ * for a set of directories recorded as a linked list
+ */
+struct afm_udb {
+ struct afm_apps applications; /* the data about applications */
+ int refcount; /* count of references to the structure */
+ int system; /* is managing system units? */
+ int user; /* is managing user units? */
+ size_t prefixlen; /* length of the prefix */
+ char prefix[1]; /* filtering prefix */
+};
+
+/*
+ * The structure afm_updt is internally used for updates
+ */
+struct afm_updt {
+ struct afm_udb *afudb;
+ struct afm_apps applications;
+};
+
+/*
+ * Release the data of the afm_apps object 'apps'.
+ */
+static void apps_put(struct afm_apps *apps)
+{
+ json_object_put(apps->prvarr);
+ json_object_put(apps->pubarr);
+ json_object_put(apps->pubobj);
+ json_object_put(apps->prvobj);
+}
+
+/*
+ * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
+ * Returns 0 on success or -1 on error.
+ */
+static int add_field(
+ struct json_object *priv,
+ struct json_object *pub,
+ const char *name,
+ const char *value
+)
+{
+ long int ival;
+ char *end;
+ struct json_object *v;
+
+ /* try to adapt the value to its type */
+ errno = 0;
+ ival = strtol(value, &end, 10);
+ if (*value && !*end && !errno) {
+ /* integer value */
+ v = json_object_new_int64(ival);
+ } else {
+ /* string value */
+ v = json_object_new_string(value);
+ }
+ if (!v) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* add the value */
+ if (name[0] == '-') {
+ json_object_object_add(priv, &name[1], v);
+ } else {
+ json_object_object_add(priv, name, json_object_get(v));
+ json_object_object_add(pub, name, v);
+ }
+ return 0;
+}
+
+/*
+ * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
+ * Returns 0 on success or -1 on error.
+ */
+static int add_fields_of_content(
+ struct json_object *priv,
+ struct json_object *pub,
+ char *content,
+ size_t length
+)
+{
+ char *name, *value, *read, *write;
+
+ read = strstr(content, x_afm_prefix);
+ while (read) {
+ name = read + x_afm_prefix_length;
+ value = strchr(name, '=');
+ if (value == NULL)
+ read = strstr(name, x_afm_prefix);
+ else {
+ *value++ = 0;
+ read = write = value;
+ while(*read && *read != '\n') {
+ if (read[0] != '\\')
+ *write++ = *read++;
+ else {
+ switch(*++read) {
+ case 'n': *write++ = '\n'; break;
+ case '\n': *write++ = ' '; break;
+ default: *write++ = '\\'; *write++ = *read; break;
+ }
+ read += !!*read;
+ }
+ }
+ read = strstr(read, x_afm_prefix);
+ *write = 0;
+ if (add_field(priv, pub, name, value) < 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Adds the application widget 'desc' of the directory 'path' to the
+ * afm_apps object 'apps'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ */
+static int addunit(
+ struct afm_apps *apps,
+ int isuser,
+ const char *unitpath,
+ const char *unitname,
+ char *content,
+ size_t length
+)
+{
+ struct json_object *priv, *pub, *id;
+ const char *strid;
+
+ /* create the application structure */
+ priv = json_object_new_object();
+ if (!priv)
+ return -1;
+
+ pub = json_object_new_object();
+ if (!pub)
+ goto error;
+
+ /* adds the values */
+ if (add_fields_of_content(priv, pub, content, length)
+ || add_field(priv, pub, key_unit_path, unitpath)
+ || add_field(priv, pub, key_unit_name, unitname)
+ || add_field(priv, pub, key_unit_scope, isuser ? scope_user : scope_system))
+ goto error;
+
+ /* get the id */
+ if (!json_object_object_get_ex(pub, key_id, &id)) {
+ errno = EINVAL;
+ goto error;
+ }
+ strid = json_object_get_string(id);
+
+ /* record the application structure */
+ json_object_get(pub);
+ json_object_array_add(apps->pubarr, pub);
+ json_object_object_add(apps->pubobj, strid, pub);
+ json_object_get(priv);
+ json_object_array_add(apps->prvarr, priv);
+ json_object_object_add(apps->prvobj, strid, priv);
+ return 0;
+
+error:
+ json_object_put(pub);
+ json_object_put(priv);
+ return -1;
+}
+
+/*
+ * called for each unit
+ */
+static int update_cb(void *closure, const char *name, const char *path, int isuser)
+{
+ struct afm_updt *updt = closure;
+ char *content;
+ size_t length;
+ int rc;
+
+ /* prefix filtering */
+ length = updt->afudb->prefixlen;
+ if (length && strncmp(updt->afudb->prefix, name, length))
+ return 0;
+
+ /* only services */
+ length = strlen(name);
+ if (length < service_extension_length || strcmp(service_extension, name + length - service_extension_length))
+ return 0;
+
+ /* reads the file */
+ rc = getfile(path, &content, &length);
+ if (rc < 0)
+ return rc;
+
+ /* process the file */
+ rc = addunit(&updt->applications, isuser, path, name, content, length);
+ free(content);
+ return rc;
+}
+
+/*
+ * Creates an afm_udb object and returns it with one reference added.
+ * Return NULL with errno = ENOMEM if memory exhausted.
+ */
+struct afm_udb *afm_udb_create(int sys, int usr, const char *prefix)
+{
+ size_t length;
+ struct afm_udb *afudb;
+
+ length = prefix ? strlen(prefix) : 0;
+ afudb = malloc(length + sizeof * afudb);
+ if (afudb == NULL)
+ errno = ENOMEM;
+ else {
+ afudb->refcount = 1;
+ afudb->applications.prvarr = NULL;
+ afudb->applications.pubarr = NULL;
+ afudb->applications.pubobj = NULL;
+ afudb->applications.prvobj = NULL;
+ afudb->system = sys;
+ afudb->user = usr;
+ afudb->prefixlen = length;
+ if (length)
+ memcpy(afudb->prefix, prefix, length);
+ afudb->prefix[length] = 0;
+ if (afm_udb_update(afudb) < 0) {
+ afm_udb_unref(afudb);
+ afudb = NULL;
+ }
+ }
+ return afudb;
+}
+
+/*
+ * Adds a reference to an existing afm_udb.
+ */
+void afm_udb_addref(struct afm_udb *afudb)
+{
+ assert(afudb);
+ afudb->refcount++;
+}
+
+/*
+ * Removes a reference to an existing afm_udb object.
+ * Removes the objet if there no more reference to it.
+ */
+void afm_udb_unref(struct afm_udb *afudb)
+{
+ assert(afudb);
+ if (!--afudb->refcount) {
+ /* no more reference, clean the memory used by the object */
+ apps_put(&afudb->applications);
+ free(afudb);
+ }
+}
+
+/*
+ * Regenerate the list of applications of the afm_bd object 'afudb'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ */
+int afm_udb_update(struct afm_udb *afudb)
+{
+ struct afm_updt updt;
+ struct afm_apps tmp;
+
+ /* lock the db */
+ afm_udb_addref(afudb);
+ updt.afudb = afudb;
+
+ /* create the result */
+ updt.applications.prvarr = json_object_new_array();
+ updt.applications.pubarr = json_object_new_array();
+ updt.applications.pubobj = json_object_new_object();
+ updt.applications.prvobj = json_object_new_object();
+ if (updt.applications.pubarr == NULL
+ || updt.applications.prvarr == NULL
+ || updt.applications.pubobj == NULL
+ || updt.applications.prvobj == NULL) {
+ errno = ENOMEM;
+ goto error;
+ }
+
+ /* scan the units */
+ if (afudb->user)
+ if (systemd_unit_list(1, update_cb, &updt) < 0)
+ goto error;
+ if (afudb->system)
+ if (systemd_unit_list(0, update_cb, &updt) < 0)
+ goto error;
+
+ /* commit the result */
+ tmp = afudb->applications;
+ afudb->applications = updt.applications;
+ apps_put(&tmp);
+ afm_udb_addref(afudb);
+ return 0;
+
+error:
+ apps_put(&updt.applications);
+ afm_udb_addref(afudb);
+ return -1;
+}
+
+/*
+ * Get the list of the applications private data of the afm_udb object 'afudb'.
+ * The list is returned as a JSON-array that must be released using
+ * 'json_object_put'.
+ * Returns NULL in case of error.
+ */
+struct json_object *afm_udb_applications_private(struct afm_udb *afudb)
+{
+ return json_object_get(afudb->applications.prvarr);
+}
+
+/*
+ * Get the list of the applications public data of the afm_udb object 'afudb'.
+ * The list is returned as a JSON-array that must be released using
+ * 'json_object_put'.
+ * Returns NULL in case of error.
+ */
+struct json_object *afm_udb_applications_public(struct afm_udb *afudb)
+{
+ return json_object_get(afudb->applications.pubarr);
+}
+
+/*
+ * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
+ * It returns a JSON-object that must be released using 'json_object_put'.
+ * Returns NULL in case of error.
+ */
+static struct json_object *get_no_case(struct json_object *object, const char *id)
+{
+ struct json_object *result;
+ struct json_object_iter i;
+
+ /* search case sensitively */
+ if (json_object_object_get_ex(object, id, &result))
+ return json_object_get(result);
+
+ /* fallback to a case insensitive search */
+ json_object_object_foreachC(object, i) {
+ if (!strcasecmp(i.key, id))
+ return json_object_get(i.val);
+ }
+ return NULL;
+}
+
+/*
+ * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
+ * It returns a JSON-object that must be released using 'json_object_put'.
+ * Returns NULL in case of error.
+ */
+struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id)
+{
+ return get_no_case(afudb->applications.prvobj, id);
+}
+
+/*
+ * Get the public data of the applications of 'id' in the afm_udb object 'afudb'.
+ * It returns a JSON-object that must be released using 'json_object_put'.
+ * Returns NULL in case of error.
+ */
+struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
+ const char *id)
+{
+ return get_no_case(afudb->applications.pubobj, id);
+}
+
+
+
+#if defined(TESTAPPFWK)
+#include <stdio.h>
+int main()
+{
+struct afm_udb *afudb = afm_udb_create(1, 1, NULL);
+printf("array = %s\n", json_object_to_json_string_ext(afudb->applications.pubarr, 3));
+printf("pubobj = %s\n", json_object_to_json_string_ext(afudb->applications.pubobj, 3));
+printf("prvobj = %s\n", json_object_to_json_string_ext(afudb->applications.prvobj, 3));
+return 0;
+}
+#endif
+