Enforce numeric application IDs
[src/app-framework-main.git] / src / wgtpkg-unit.c
index 48cf3bb..e01faa9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- Copyright 2016, 2017 IoT.bzh
+ Copyright (C) 2016-2019 IoT.bzh
 
  author: José Bollo <jose.bollo@iot.bzh>
 
@@ -28,7 +28,7 @@
 #include <limits.h>
 
 #include <json-c/json.h>
+
 #include "verbose.h"
 #include "utils-file.h"
 
@@ -38,6 +38,7 @@
 #include "utils-systemd.h"
 
 #include "wgtpkg-unit.h"
+#include "wgt-strings.h"
 
 #if 0
 #include <ctype.h>
@@ -53,8 +54,8 @@ static char *template;
  * Returns 1 if 'text' matches the 'pattern' or else returns 0.
  * When returning 1 and 'after' isn't NULL, the pointer to the
  * first character after the pettern in 'text' is stored in 'after'.
- * The characters '\n' and ' ' have a special mening in the search:
- *  * '\n': matches any space or tabs (including none) followed 
+ * The characters '\n' and ' ' have a special meaning in the search:
+ *  * '\n': matches any space or tabs (including none) followed
  *          either by '\n' or '\0' (end of the string)
  *  * ' ': matches any space or tabs but at least one.
  */
@@ -116,12 +117,13 @@ static size_t pack(char *text, char purge)
        char cont;     /* flag telling whether the line continues the previous one */
        char nextcont; /* flag telling whether the line will continues the next one */
 
-       cont = 0;
+       nextcont = 0;
        c = *(write = read = text);
 
        /* iteration over lines */
        while (c) {
                /* computes emit, nextcont, emit and start for the current line */
+               cont = nextcont;
                emit = nextcont = 0;
                start = NULL;
                begin = read;
@@ -140,7 +142,7 @@ static size_t pack(char *text, char purge)
                if (c)
                        c = *++read;
                /* emit the line if not empty */
-               if (emit) {
+               if (emit || (cont && !nextcont)) {
                        /* removes the blanks on the left of not continuing lines */
                        if (!cont && start)
                                begin = start;
@@ -155,7 +157,6 @@ static size_t pack(char *text, char purge)
                                        *write++ = *begin++;
                        }
                }
-               cont = nextcont;
        }
        *write = 0;
        return (size_t)(write - text);
@@ -217,7 +218,7 @@ static int process_one_unit(char *spec, struct unitdesc *desc)
                        desc->type = unittype_service;
                        name = nsrv;
                }
-               len = (size_t)(strchrnul(name, '\n') - name);
+               len = strcspn(name, " \t\n");
                desc->name = strndup(name, len);
                desc->name_length = desc->name ? len : 0;
        } else {
@@ -227,7 +228,7 @@ static int process_one_unit(char *spec, struct unitdesc *desc)
        }
 
        if (iswanted) {
-               len = (size_t)(strchrnul(wanted, '\n') - wanted);
+               len = strcspn(wanted, " \t\n");
                desc->wanted_by = strndup(wanted, len);
                desc->wanted_by_length = len;
        } else {
@@ -249,42 +250,75 @@ static int process_one_unit(char *spec, struct unitdesc *desc)
  * with its given 'closure' and the array descripbing the units.
  * Return 0 in case of success or a negative value in case of error.
  */
-static int process_all_units(char *corpus, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure)
+static int process_all_units(char *corpus, const struct unitconf *conf, int (*process)(void *closure, const struct generatedesc *desc), void *closure, struct json_object *jdesc)
 {
        int rc, rc2;
-       unsigned n;
-       char *beg, *end;
-       struct unitdesc *descs, *d;
-
-       descs = NULL;
-       n = 0;
+       char *beg, *end, *befbeg, *aftend;
+       struct unitdesc *u;
+       struct generatedesc gdesc;
+
+       gdesc.conf = conf;
+       gdesc.desc = jdesc;
+       gdesc.units = NULL;
+       gdesc.nunits = 0;
        rc = rc2 = 0;
 
        /* while there is a unit in the corpus */
-       while(offset(corpus, "%begin systemd-unit\n", &beg)) {
-
-               /* get the end of the unit */
-               end = offset(beg, "%end systemd-unit\n", &corpus);
+       for(;;) {
+               befbeg = offset(corpus, "%begin ", &beg);
+               end = offset(corpus, "%end ", &aftend);
+               if (!befbeg) {
+                       if (end) {
+                               /* %end detected without %begin */
+                               ERROR("unexpected %%end at end");
+                               rc = rc ? :-EINVAL;
+                       }
+                       break;
+               }
                if (!end) {
                        /* unterminated unit !! */
-                       ERROR("unterminated unit description!! %s", beg);
+                       ERROR("unterminated unit description!!");
                        corpus = beg;
                        rc2 = -EINVAL;
+               } else if (end < befbeg) {
+                       /* sequence %end ... %begin detected !! */
+                       ERROR("unexpected %%end before %%begin");
+                       corpus = aftend;
+                       rc2 = -EINVAL;
                } else {
-                       /* separate the unit from the corpus */
-                       *end = 0;
-
-                       /* allocates a descriptor for the unit */
-                       d = realloc(descs, (n + 1) * sizeof *descs);
-                       if (d == NULL)
-                               rc2 = -ENOMEM;
-                       else {
-                               /* creates the unit description */
-                               memset(&d[n], 0, sizeof *d);
-                               descs = d;
-                               rc2 = process_one_unit(beg, &descs[n]);
-                               if (rc2 >= 0)
-                                       n++;
+                       befbeg =  offset(beg, "%begin ", NULL);
+                       if (befbeg && befbeg < end) {
+                               /* sequence %begin ... %begin ... %end detected !! */
+                               ERROR("unexpected %%begin after %%begin");
+                               corpus = beg;
+                               rc2 = -EINVAL;
+                       } else {
+                               *end = 0;
+                               corpus = aftend;
+                               if (matches("systemd-unit\n", beg, &beg)) {
+                                       if (!matches("systemd-unit\n", aftend, &corpus)) {
+                                               /* end doesnt match */
+                                               ERROR("unmatched %%begin systemd-unit (matching end mismatch)");
+                                               rc2 = -EINVAL;
+                                       } else {
+                                               /* allocates a descriptor for the unit */
+                                               u = realloc((void*)gdesc.units, ((unsigned)gdesc.nunits + 1) * sizeof *gdesc.units);
+                                               if (u == NULL)
+                                                       rc2 = -ENOMEM;
+                                               else {
+                                                       /* creates the unit description */
+                                                       gdesc.units = u;
+                                                       u = &u[gdesc.nunits];
+                                                       memset(u, 0, sizeof *u);
+                                                       rc2 = process_one_unit(beg, u);
+                                                       if (rc2 >= 0)
+                                                               gdesc.nunits++;
+                                               }
+                                       }
+                               } else {
+                                       ERROR("unexpected %%begin name");
+                                       rc2 = -EINVAL;
+                               }
                        }
                }
                /* records the error if there is an error */
@@ -296,14 +330,14 @@ static int process_all_units(char *corpus, int (*process)(void *closure, const s
 
        /* call the function that processes the units */
        if (rc == 0 && process)
-               rc = process(closure, descs, n);
+               rc = process(closure, &gdesc);
 
        /* cleanup and frees */
-       while(n) {
-               free((char *)(descs[--n].name));
-               free((char *)(descs[n].wanted_by));
+       while(gdesc.nunits) {
+               free((void*)(gdesc.units[--gdesc.nunits].name));
+               free((void*)(gdesc.units[gdesc.nunits].wanted_by));
        }
-       free(descs);
+       free((void*)gdesc.units);
 
        return rc;
 }
@@ -311,7 +345,7 @@ static int process_all_units(char *corpus, int (*process)(void *closure, const s
 /*
  * Clear the unit generator
  */
-void unit_generator_off()
+void unit_generator_close_template()
 {
        free(template);
        template = NULL;
@@ -321,13 +355,13 @@ void unit_generator_off()
  * Initialises the unit generator with the content of the file of path 'filename'.
  * Returns 0 in case of success or a negative number in case of error.
  */
-int unit_generator_on(const char *filename)
+int unit_generator_open_template(const char *filename)
 {
        size_t size;
        char *tmp;
        int rc;
 
-       unit_generator_off();
+       unit_generator_close_template();
        rc = getfile(filename ? : FWK_UNIT_CONF, &template, NULL);
        if (!rc) {
                size = pack(template, ';');
@@ -338,28 +372,69 @@ int unit_generator_on(const char *filename)
        return rc;
 }
 
+static int add_metadata(struct json_object *jdesc, const struct unitconf *conf)
+{
+       struct json_object *targets, *targ;
+       char portstr[30], afidstr[30];
+       int port, afid, i, n;
+
+       if (json_object_object_get_ex(jdesc, string_targets, &targets)) {
+               n = json_object_array_length(targets);
+               for (i = 0 ; i < n ; i++) {
+                       targ = json_object_array_get_idx(targets, i);
+                       if (!conf->new_afid) {
+                               afid = 0;
+                               port = 0;
+                       } else {
+                               afid = conf->new_afid();
+                               if (afid < 0)
+                                       return afid;
+                               port = conf->base_http_ports + afid;
+                       }
+                       sprintf(afidstr, "%d", afid);
+                       sprintf(portstr, "%d", port);
+                       if (!j_add_many_strings_m(targ,
+                               "#metatarget.http-port", portstr,
+                               "#metatarget.afid", afidstr,
+                               NULL))
+                               return -1;
+               }
+       }
+
+       return  j_add_many_strings_m(jdesc,
+               "#metadata.install-dir", conf->installdir,
+               "#metadata.icons-dir", conf->icondir,
+               NULL) ? 0 : -1;
+}
+
 /*
- * Applies the object 'jdesc' to the current unit generator.
+ * Applies the object 'jdesc' augmented of meta data coming
+ * from 'conf' to the current unit generator.
  * The current unit generator will be set to the default one if not unit
- * was previously set using the function 'unit_generator_on'.
+ * was previously set using the function 'unit_generator_open_template'.
  * The callback function 'process' is then called with the
  * unit descriptors array and the expected closure.
  * Return what returned process in case of success or a negative
  * error code.
  */
-int unit_generator_process(struct json_object *jdesc, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure)
+int unit_generator_process(struct json_object *jdesc, const struct unitconf *conf, int (*process)(void *closure, const struct generatedesc *desc), void *closure)
 {
        int rc;
        size_t size;
        char *instance;
 
-       rc = template ? 0 : unit_generator_on(NULL);
-       if (!rc) {
-               instance = NULL;
-               rc = apply_mustach(template, jdesc, &instance, &size);
-               if (!rc)
-                       rc = process_all_units(instance, process, closure);
-               free(instance);
+       rc = add_metadata(jdesc, conf);
+       if (rc)
+               ERROR("can't set the metadata. %m");
+       else {
+               rc = template ? 0 : unit_generator_open_template(NULL);
+               if (!rc) {
+                       instance = NULL;
+                       rc = apply_mustach(template, jdesc, &instance, &size);
+                       if (!rc)
+                               rc = process_all_units(instance, conf, process, closure, jdesc);
+                       free(instance);
+               }
        }
        return rc;
 }
@@ -419,52 +494,26 @@ static int get_wants_target(char *path, size_t pathlen, const struct unitdesc *d
        return rc;
 }
 
-static int do_send_reload(const struct unitdesc descs[], unsigned count)
-{
-       unsigned i;
-       int reloadsys, reloadusr;
-
-       reloadsys = reloadusr = 0;
-       for (i = 0 ; i < count ; i++) {
-               if (descs[i].wanted_by != NULL) {
-                       switch (descs[i].scope) {
-                       case unitscope_user:
-                               reloadusr = 1;
-                               break;
-                       case unitscope_system:
-                               reloadsys = 1;
-                               break;
-                       default:
-                               break;
-                       }
-               }
-       }
-
-       if (reloadusr)
-               reloadusr = systemd_daemon_reload(1);
-       if (reloadsys)
-               reloadsys = systemd_daemon_reload(0);
-       return reloadsys ? : reloadusr ? : 0;
-}
-
-static int do_uninstall_units(void *closure, const struct unitdesc descs[], unsigned count)
+static int do_uninstall_units(void *closure, const struct generatedesc *desc)
 {
        int rc, rc2;
-       unsigned i;
+       int i;
        char path[PATH_MAX];
+       const struct unitdesc *u;
 
        rc = 0;
-       for (i = 0 ; i < count ; i++) {
-               rc2 = check_unit_desc(&descs[i], 0);
+       for (i = 0 ; i < desc->nunits ; i++) {
+               u = &desc->units[i];
+               rc2 = check_unit_desc(u, 0);
                if (rc2 == 0) {
-                       rc2 = get_unit_path(path, sizeof path, &descs[i]);
+                       rc2 = get_unit_path(path, sizeof path, u);
                        if (rc2 >= 0) {
                                rc2 = unlink(path);
                        }
                        if (rc2 < 0 && rc == 0)
                                rc = rc2;
-                       if (descs[i].wanted_by != NULL) {
-                               rc2 = get_wants_path(path, sizeof path, &descs[i]);
+                       if (u->wanted_by != NULL) {
+                               rc2 = get_wants_path(path, sizeof path, u);
                                if (rc2 >= 0)
                                        rc2 = unlink(path);
                        }
@@ -472,29 +521,28 @@ static int do_uninstall_units(void *closure, const struct unitdesc descs[], unsi
                if (rc2 < 0 && rc == 0)
                        rc = rc2;
        }
-       rc2 = do_send_reload(descs, count);
-       if (rc2 < 0 && rc == 0)
-               rc = rc2;
        return rc;
 }
 
-static int do_install_units(void *closure, const struct unitdesc descs[], unsigned count)
+static int do_install_units(void *closure, const struct generatedesc *desc)
 {
        int rc;
-       unsigned i;
+       int i;
        char path[PATH_MAX + 1], target[PATH_MAX + 1];
+       const struct unitdesc *u;
 
        i = 0;
-       while (i < count) {
-               rc = check_unit_desc(&descs[i], 1);
+       while (i < desc->nunits) {
+               u = &desc->units[i];
+               rc = check_unit_desc(u, 1);
                if (!rc) {
-                       rc = get_unit_path(path, sizeof path, &descs[i]);
+                       rc = get_unit_path(path, sizeof path, u);
                        if (rc >= 0) {
-                               rc = putfile(path, descs[i].content, descs[i].content_length);
-                               if (descs[i].wanted_by != NULL) {
-                                       rc = get_wants_path(path, sizeof path, &descs[i]);
+                               rc = putfile(path, u->content, u->content_length);
+                               if (rc >= 0 && u->wanted_by != NULL) {
+                                       rc = get_wants_path(path, sizeof path, u);
                                        if (rc >= 0) {
-                                               rc = get_wants_target(target, sizeof target, &descs[i]);
+                                               rc = get_wants_target(target, sizeof target, u);
                                                if (rc >= 0) {
                                                        unlink(path); /* TODO? check? */
                                                        rc = symlink(target, path);
@@ -507,34 +555,18 @@ static int do_install_units(void *closure, const struct unitdesc descs[], unsign
                if (rc < 0)
                        goto error;
        }
-       rc = do_send_reload(descs, count);
-       if (rc < 0)
-               goto error;
        return 0;
 error:
-       do_uninstall_units(closure, descs, i);
+       i = errno;
+       do_uninstall_units(closure, desc);
+       errno = i;
        return rc;
 }
 
-static int add_metadata(struct json_object *jdesc, const char *installdir, const char *icondir, int port)
-{
-       char portstr[30];
-
-       sprintf(portstr, "%d", port);
-       return  j_add_many_strings_m(jdesc,
-               "#metadata.install-dir", installdir,
-               "#metadata.app-data-dir", "%h/app-data",
-               "#metadata.icons-dir", icondir,
-               "#metadata.http-port", portstr,
-               NULL) ? 0 : -1;
-}
-
 static int do_install_uninstall(
                struct wgt_info *ifo,
-               const char *installdir,
-               const char *icondir,
-               int port,
-               int (*doer)(void *, const struct unitdesc[], unsigned)
+               const struct unitconf *conf,
+               int (*doer)(void *, const struct generatedesc *)
 )
 {
        int rc;
@@ -544,26 +576,19 @@ static int do_install_uninstall(
        if (!jdesc)
                rc = -1;
        else {
-               rc = add_metadata(jdesc, installdir, icondir, port);
-               if (rc)
-                       ERROR("can't set the metadata. %m");
-               else {
-                       rc = unit_generator_process(jdesc, doer, NULL);
-                       if (rc)
-                               ERROR("can't install units, error %d", rc);
-               }
+               rc = unit_generator_process(jdesc, conf, doer, NULL);
                json_object_put(jdesc);
        }
        return rc;
 }
 
-int unit_install(struct wgt_info *ifo, const char *installdir, const char *icondir, int port)
+int unit_install(struct wgt_info *ifo, const struct unitconf *conf)
 {
-       return do_install_uninstall(ifo, installdir, icondir, port, do_install_units);
+       return do_install_uninstall(ifo, conf, do_install_units);
 }
 
-int unit_uninstall(struct wgt_info *ifo)
+int unit_uninstall(struct wgt_info *ifo, const struct unitconf *conf)
 {
-       return do_install_uninstall(ifo, "", "", 0, do_uninstall_units);
+       return do_install_uninstall(ifo, conf, do_uninstall_units);
 }