+/**************** 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_uninstall_units(void *closure, const struct unitdesc descs[], unsigned count)
+{
+ int rc, rc2;
+ unsigned i;
+ char path[PATH_MAX];
+
+ for (i = 0 ; i < count ; i++) {
+ rc = check_unit_desc(&descs[i], 0);
+ if (rc == 0) {
+ rc = get_unit_path(path, sizeof path, &descs[i]);
+ if (rc >= 0) {
+ rc = unlink(path);
+ }
+ if (descs[i].wanted_by != NULL) {
+ rc2 = get_wants_path(path, sizeof path, &descs[i]);
+ if (rc2 >= 0)
+ rc2 = unlink(path);
+ rc = rc < 0 ? rc : rc2;
+ }
+ }
+ }
+ return 0;
+}
+
+static int do_install_units(void *closure, const struct unitdesc descs[], unsigned count)
+{
+ int rc;
+ unsigned i;
+ char path[PATH_MAX + 1], target[PATH_MAX + 1];
+
+ i = 0;
+ while (i < count) {
+ rc = check_unit_desc(&descs[i], 1);
+ if (!rc) {
+ rc = get_unit_path(path, sizeof path, &descs[i]);
+ 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]);
+ if (rc >= 0) {
+ rc = get_wants_target(target, sizeof target, &descs[i]);
+ if (rc >= 0) {
+ unlink(path); /* TODO? check? */
+ rc = symlink(target, path);
+ }
+ }
+ }
+ i++;
+ }
+ }
+ if (rc < 0) {
+ do_uninstall_units(closure, descs, i);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+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)
+)
+{
+ int rc;
+ struct json_object *jdesc;
+
+ jdesc = wgt_info_to_json(ifo);
+ 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);
+ }
+ json_object_put(jdesc);
+ }
+ return rc;
+}
+
+int unit_install(struct wgt_info *ifo, const char *installdir, const char *icondir, int port)
+{
+ return do_install_uninstall(ifo, installdir, icondir, port, do_install_units);
+}
+
+int unit_uninstall(struct wgt_info *ifo)
+{
+ return do_install_uninstall(ifo, "", "", 0, do_uninstall_units);
+}
+