Implement deinstallation of units
[src/app-framework-main.git] / src / wgtpkg-unit.c
index 26cf324..3f2095c 100644 (file)
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <limits.h>
+
+#include <json-c/json.h>
  
 #include "verbose.h"
 #include "utils-file.h"
 
 #include "wgtpkg-mustach.h"
+#include "utils-json.h"
+#include "wgt-json.h"
+#include "utils-systemd.h"
+
 #include "wgtpkg-unit.h"
 
 #if 0
@@ -109,12 +116,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;
@@ -133,7 +141,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;
@@ -148,7 +156,6 @@ static size_t pack(char *text, char purge)
                                        *write++ = *begin++;
                        }
                }
-               cont = nextcont;
        }
        *write = 0;
        return (size_t)(write - text);
@@ -183,8 +190,8 @@ static char *offset(char *text, const char *pattern, char **args)
  */
 static int process_one_unit(char *spec, struct unitdesc *desc)
 {
-       char *nsoc, *nsrv, *name;
-       int isuser, issystem, issock, isserv;
+       char *nsoc, *nsrv, *name, *wanted;
+       int isuser, issystem, issock, isserv, iswanted;
        size_t len;
 
        /* finds the configuration directive of the unit */
@@ -192,6 +199,7 @@ static int process_one_unit(char *spec, struct unitdesc *desc)
        issystem = !!offset(spec, "%systemd-unit system\n", NULL);
        issock  = !!offset(spec, "%systemd-unit socket ", &nsoc);
        isserv  = !!offset(spec, "%systemd-unit service ", &nsrv);
+       iswanted = !!offset(spec, "%systemd-unit wanted-by ", &wanted);
 
        /* check the unit scope */
        if ((isuser + issystem) == 1) {
@@ -218,6 +226,15 @@ static int process_one_unit(char *spec, struct unitdesc *desc)
                desc->name_length = 0;
        }
 
+       if (iswanted) {
+               len = (size_t)(strchrnul(wanted, '\n') - wanted);
+               desc->wanted_by = strndup(wanted, len);
+               desc->wanted_by_length = len;
+       } else {
+               desc->wanted_by = NULL;
+               desc->wanted_by_length = 0;
+       }
+
        desc->content = spec;
        desc->content_length = pack(spec, '%');
 
@@ -232,14 +249,14 @@ 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)
 {
-       int rc, rc2;
-       unsigned n;
+       int n, rc, rc2;
        char *beg, *end;
-       struct unitdesc *descs, *d;
+       struct unitdesc *units, *u;
+       struct generatedesc gdesc;
 
-       descs = NULL;
+       units = NULL;
        n = 0;
        rc = rc2 = 0;
 
@@ -258,14 +275,15 @@ static int process_all_units(char *corpus, int (*process)(void *closure, const s
                        *end = 0;
 
                        /* allocates a descriptor for the unit */
-                       d = realloc(descs, (n + 1) * sizeof *descs);
-                       if (d == NULL)
+                       u = realloc(units, ((unsigned)n + 1) * sizeof *units);
+                       if (u == NULL)
                                rc2 = -ENOMEM;
                        else {
                                /* creates the unit description */
-                               memset(&d[n], 0, sizeof *d);
-                               descs = d;
-                               rc2 = process_one_unit(beg, &descs[n]);
+                               units = u;
+                               u = &u[n];
+                               memset(u, 0, sizeof *u);
+                               rc2 = process_one_unit(beg, u);
                                if (rc2 >= 0)
                                        n++;
                        }
@@ -278,13 +296,19 @@ 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);
+       if (rc == 0 && process) {
+               gdesc.conf = conf;
+               gdesc.units = units;
+               gdesc.nunits = n;
+               rc = process(closure, &gdesc);
+       }
 
        /* cleanup and frees */
-       while(n)
-               free((char *)(descs[--n].name));
-       free(descs);
+       while(n) {
+               free((char *)(units[--n].name));
+               free((char *)(units[n].wanted_by));
+       }
+       free(units);
 
        return rc;
 }
@@ -299,7 +323,7 @@ void unit_generator_off()
 }
 
 /*
- * Initialises the unit generator with 'filename'.
+ * 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)
@@ -319,8 +343,22 @@ int unit_generator_on(const char *filename)
        return rc;
 }
 
+static int add_metadata(struct json_object *jdesc, const struct unitconf *conf)
+{
+       char portstr[30];
+
+       sprintf(portstr, "%d", conf->port);
+       return  j_add_many_strings_m(jdesc,
+               "#metadata.install-dir", conf->installdir,
+               "#metadata.app-data-dir", "%h/app-data",
+               "#metadata.icons-dir", conf->icondir,
+               "#metadata.http-port", portstr,
+               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'.
  * The callback function 'process' is then called with the
@@ -328,20 +366,214 @@ int unit_generator_on(const char *filename)
  * 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_on(NULL);
+               if (!rc) {
+                       instance = NULL;
+                       rc = apply_mustach(template, jdesc, &instance, &size);
+                       if (!rc)
+                               rc = process_all_units(instance, conf, process, closure);
+                       free(instance);
+               }
        }
        return rc;
 }
 
+/**************** SPECIALIZED PART *****************************/
+
+static int check_unit_desc(const struct unitdesc *desc, int tells)
+{
+       if (desc->scope != unitscope_unknown && desc->type != unittype_unknown && desc->name != NULL)
+               return 0;
+
+       if (tells) {
+               if (desc->scope == unitscope_unknown)
+                       ERROR("unit of unknown scope");
+               if (desc->type == unittype_unknown)
+                       ERROR("unit of unknown type");
+               if (desc->name == NULL)
+                       ERROR("unit of unknown name");
+       }
+       errno = EINVAL;
+       return -1;
+}
+
+static int get_unit_path(char *path, size_t pathlen, const struct unitdesc *desc)
+{
+       int rc = systemd_get_unit_path(
+                       path, pathlen, desc->scope == unitscope_user,
+                       desc->name, desc->type == unittype_socket ? "socket" : "service");
+
+       if (rc < 0)
+               ERROR("can't get the unit path for %s", desc->name);
+
+       return rc;
+}
+
+static int get_wants_path(char *path, size_t pathlen, const struct unitdesc *desc)
+{
+       int rc = systemd_get_wants_path(
+                       path, pathlen, desc->scope == unitscope_user, desc->wanted_by,
+                       desc->name, desc->type == unittype_socket ? "socket" : "service");
+
+       if (rc < 0)
+               ERROR("can't get the wants path for %s and %s", desc->name, desc->wanted_by);
+
+       return rc;
+}
+
+static int get_wants_target(char *path, size_t pathlen, const struct unitdesc *desc)
+{
+       int rc = systemd_get_wants_target(
+                       path, pathlen,
+                       desc->name, desc->type == unittype_socket ? "socket" : "service");
+
+       if (rc < 0)
+               ERROR("can't get the wants target for %s", desc->name);
+
+       return rc;
+}
+
+static int do_send_reload(const struct generatedesc *desc)
+{
+       int i;
+       int reloadsys, reloadusr;
+       const struct unitdesc *u;
+
+       reloadsys = reloadusr = 0;
+       for (i = 0 ; i < desc->nunits ; i++) {
+               u = &desc->units[i];
+               if (u->wanted_by != NULL) {
+                       switch (u->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 generatedesc *desc)
+{
+       int rc, rc2;
+       int i;
+       char path[PATH_MAX];
+       const struct unitdesc *u;
+
+       rc = 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, u);
+                       if (rc2 >= 0) {
+                               rc2 = unlink(path);
+                       }
+                       if (rc2 < 0 && rc == 0)
+                               rc = rc2;
+                       if (u->wanted_by != NULL) {
+                               rc2 = get_wants_path(path, sizeof path, u);
+                               if (rc2 >= 0)
+                                       rc2 = unlink(path);
+                       }
+               }
+               if (rc2 < 0 && rc == 0)
+                       rc = rc2;
+       }
+       rc2 = do_send_reload(desc);
+       if (rc2 < 0 && rc == 0)
+               rc = rc2;
+       return rc;
+}
+
+static int do_install_units(void *closure, const struct generatedesc *desc)
+{
+       int rc;
+       int i;
+       char path[PATH_MAX + 1], target[PATH_MAX + 1];
+       const struct unitdesc *u;
+
+       i = 0;
+       while (i < desc->nunits) {
+               u = &desc->units[i];
+               rc = check_unit_desc(u, 1);
+               if (!rc) {
+                       rc = get_unit_path(path, sizeof path, u);
+                       if (rc >= 0) {
+                               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, u);
+                                               if (rc >= 0) {
+                                                       unlink(path); /* TODO? check? */
+                                                       rc = symlink(target, path);
+                                               }
+                                       }
+                               }
+                               i++;
+                       }
+               }
+               if (rc < 0)
+                       goto error;
+       }
+       rc = do_send_reload(desc);
+       if (rc < 0)
+               goto error;
+       return 0;
+error:
+       i = errno;
+       do_uninstall_units(closure, desc);
+       errno = i;
+       return rc;
+}
+
+static int do_install_uninstall(
+               struct wgt_info *ifo,
+               const struct unitconf *conf,
+               int (*doer)(void *, const struct generatedesc *)
+)
+{
+       int rc;
+       struct json_object *jdesc;
+
+       jdesc = wgt_info_to_json(ifo);
+       if (!jdesc)
+               rc = -1;
+       else {
+               rc = unit_generator_process(jdesc, conf, doer, NULL);
+               json_object_put(jdesc);
+       }
+       return rc;
+}
+
+int unit_install(struct wgt_info *ifo, const struct unitconf *conf)
+{
+       return do_install_uninstall(ifo, conf, do_install_units);
+}
+
+int unit_uninstall(struct wgt_info *ifo, const struct unitconf *conf)
+{
+       return do_install_uninstall(ifo, conf, do_uninstall_units);
+}
+