Start user units at the system level
[src/app-framework-main.git] / src / afm-udb.c
index 9b1a354..0a48810 100644 (file)
@@ -17,6 +17,7 @@
 */
 
 #include <stdlib.h>
+#include <stdio.h>
 #include <assert.h>
 #include <string.h>
 #include <errno.h>
@@ -89,6 +90,41 @@ static void apps_put(struct afm_apps *apps)
        json_object_put(apps->prvobj);
 }
 
+/*
+ * Append the field 'data' to the field 'name' of the 'object'.
+ * When a second append is done to one field, it is automatically
+ * transformed to an array.
+ * Return 0 in case of success or -1 in case of error.
+ */
+static int append_field(
+               struct json_object *object,
+               const char *name,
+               struct json_object *data
+)
+{
+       struct json_object *item, *array;
+
+       if (!json_object_object_get_ex(object, name, &item))
+               json_object_object_add(object, name, data);
+       else {
+               if (json_object_is_type(item, json_type_array))
+                       array = item;
+               else {
+                       array = json_object_new_array();
+                       if (!array)
+                               goto error;
+                       json_object_array_add(array, item);
+                       json_object_object_add(object, name, array);
+               }
+               json_object_array_add(array, data);
+       }
+       return 0;
+ error:
+       json_object_put(data);
+       errno = ENOMEM;
+       return -1;
+}
+
 /*
  * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
  * Returns 0 on success or -1 on error.
@@ -121,10 +157,10 @@ static int add_field(
 
        /* add the value */
        if (name[0] == '-') {
-               json_object_object_add(priv, &name[1], v);
+               append_field(priv, &name[1], v);
        } else {
-               json_object_object_add(priv, name, json_object_get(v));
-               json_object_object_add(pub, name, v);
+               append_field(priv, name, json_object_get(v));
+               append_field(pub, name, v);
        }
        return 0;
 }
@@ -189,6 +225,8 @@ static int addunit(
 {
        struct json_object *priv, *pub, *id;
        const char *strid;
+       char *un = NULL;
+       size_t len;
 
        /* create the application structure */
        priv = json_object_new_object();
@@ -199,12 +237,30 @@ static int addunit(
        if (!pub)
                goto error;
 
+       /* make the unit name */
+       len = strlen(unitname);
+       assert(len >= (sizeof service_extension - 1));
+       assert(!memcmp(&unitname[len - (sizeof service_extension - 1)], service_extension, sizeof service_extension));
+       if (unitname[len - sizeof service_extension] == '@') {
+               char buffer[40];
+               size_t l = (size_t)snprintf(buffer, sizeof buffer, "%d", (int)getuid());
+               un = malloc(len + l + 1);
+               if (!un)
+                       goto error;
+               memcpy(&un[0], unitname, len - (sizeof service_extension - 1));
+               if (l)
+                       memcpy(&un[len - (sizeof service_extension - 1)], buffer, l);
+               memcpy(&un[len - (sizeof service_extension - 1) + l], service_extension, sizeof service_extension);
+       }
+
        /* 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_name, un ? : unitname)
         || add_field(priv, pub, key_unit_scope, isuser ? scope_user : scope_system))
                goto error;
+       free(un);
+       un = NULL;
 
        /* get the id */
        if (!json_object_object_get_ex(pub, key_id, &id)) {
@@ -223,11 +279,71 @@ static int addunit(
        return 0;
 
 error:
+       free(un);
        json_object_put(pub);
        json_object_put(priv);
        return -1;
 }
 
+/*
+ * read a unit file
+ */
+static int read_unit_file(const char *path, char **content, size_t *length)
+{
+       int rc, st;
+       char c, *read, *write;
+
+       /* read the file */
+       rc = getfile(path, content, length);
+       if (rc >= 0) {
+               /* removes any comment and join lines */
+               st = 0;
+               read = write = *content;
+               for (;;) {
+                       do { c = *read++; } while (c == '\r');
+                       if (!c)
+                               break;
+                       switch (st) {
+                       case 0:
+                               if (c == ';' || c == '#') {
+                                       st = 3; /* removes lines starting with ; or # */
+                                       break;
+                               }
+                               if (c == '\n')
+                                       break; /* removes empty lines */
+enter_state_1:
+                               st = 1;
+                               /*@fallthrough@*/
+                       case 1:
+                               if (c == '\\')
+                                       st = 2;
+                               else {
+                                       *write++ = c;
+                                       if (c == '\n')
+                                               st = 0;
+                               }
+                               break;
+                       case 2:
+                               if (c == '\n')
+                                       c = ' ';
+                               else
+                                       *write++ = '\\';
+                               goto enter_state_1;
+                       case 3:
+                               if (c == '\n')
+                                       st = 0;
+                               break;
+                       }
+               }
+               if (st == 1)
+                       *write++ = '\n';
+               *write = 0;
+               *length = (size_t)(write - *content);
+               *content = realloc(*content, *length + 1);
+       }
+       return rc;
+}
+
 /*
  * called for each unit
  */
@@ -249,7 +365,7 @@ static int update_cb(void *closure, const char *name, const char *path, int isus
                return 0;
 
        /* reads the file */
-       rc = getfile(path, &content, &length);
+       rc = read_unit_file(path, &content, &length);
        if (rc < 0)
                return rc;