install: improve port detection
[src/app-framework-main.git] / src / wgtpkg-install.c
index 93c2bbc..cec3b75 100644 (file)
@@ -25,6 +25,9 @@
 #include <assert.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
 #include <sys/stat.h>
 
 #include "verbose.h"
 #include "wgtpkg-install.h"
 #include "secmgr-wrap.h"
 #include "utils-dir.h"
+#include "wgtpkg-unit.h"
+#include "utils-systemd.h"
+#include "utils-file.h"
 
 static const char* exec_type_strings[] = {
        "application/x-executable",
        "application/vnd.agl.native"
 };
 
+static const char key_afm_prefix[] = "X-AFM-";
+static const char key_http_port[] = "http-port";
+
+/*
+ * normalize unit files: remove comments, remove heading blanks,
+ * make single lines
+ */
+static void normalize_unit_file(char *content)
+{
+       char *read, *write, c;
+
+       read = write = content;
+       c = *read++;
+       while (c) {
+               switch (c) {
+               case '\n':
+               case ' ':
+               case '\t':
+                       c = *read++;
+                       break;
+               case '#':
+               case ';':
+                       do { c = *read++; } while(c && c != '\n');
+                       break;
+               default:
+                       *write++ = c;
+                       do { *write++ = c = *read++; } while(c && c != '\n');
+                       if (write - content >= 2 && write[-2] == '\\')
+                               (--write)[-1] = ' ';
+                       break;
+               }
+       }
+       *write = c;
+}
+
+static int get_port_cb(void *closure, const char *name, const char *path, int isuser)
+{
+       char *iter;
+       char *content;
+       size_t length;
+       int rc, p;
+
+       /* reads the file */
+       rc = getfile(path, &content, &length);
+       if (rc < 0)
+               return rc;
+
+       /* normalize the unit file */
+       normalize_unit_file(content);
+
+       /* process the file */
+       iter = strstr(content, key_afm_prefix);
+       while (iter) {
+               iter += sizeof key_afm_prefix - 1;
+               if (*iter == '-')
+                       iter++;
+               if (!strncmp(iter, key_http_port, sizeof key_http_port - 1)) {
+                       iter += sizeof key_http_port - 1;
+                       while(*iter && *iter != '=' && *iter != '\n')
+                               iter++;
+                       if (*iter == '=') {
+                               while(*++iter == ' ');
+                               p = atoi(iter);
+                               if (p >= 0 && p < 32768)
+                                       ((uint32_t*)closure)[p >> 5] |= (uint32_t)1 << (p & 31);
+                       }
+               }
+               iter = strstr(iter, key_afm_prefix);
+       }
+       free(content);
+       return 0;
+}
+
+static int get_port()
+{
+       int rc;
+       uint32_t ports[1024]; /* 1024 * 32 = 32768 */
+
+       memset(ports, 0, sizeof ports);
+       rc = systemd_unit_list(0, get_port_cb, &ports);
+       if (rc >= 0) {
+               rc = systemd_unit_list(1, get_port_cb, ports);
+               if (rc >= 0) {
+                       for (rc = 1024 ; rc < 32768 && !~ports[rc >> 5] ; rc += 32);
+                       if (rc == 32768) {
+                               ERROR("Can't compute a valid port");
+                               errno = EADDRNOTAVAIL;
+                               rc = -1;
+                       } else {
+                               while (1 & (ports[rc >> 5] >> (rc & 31))) rc++;
+                       }
+               }
+       }
+       return rc;
+}
+
 static int check_defined(const void *data, const char *name)
 {
        if (data)
@@ -104,25 +206,30 @@ static int set_required_permissions(struct wgt_desc_param *params, int required)
        int optional;
 
        while (params) {
-               /* check the value */
-               if (!strcmp(params->value, string_required))
-                       optional = !required;
-               else if (!strcmp(params->value, string_optional))
-                       optional = 1;
-               else {
-                       ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
-                       errno = EPERM;
-                       return -1;
-               }
-               /* set the permission */
-               if (request_permission(params->name)) {
-                       DEBUG("granted permission: %s", params->name);
-               } else if (optional) {
-                       INFO("optional permission ungranted: %s", params->name);
+               /* check if target */
+               if (!strcmp(params->name, string_sharp_target)) {
+                       /* do nothing when #target */
                } else {
-                       ERROR("ungranted permission required: %s", params->name);
-                       errno = EPERM;
-                       return -1;
+                       /* check the value */
+                       if (!strcmp(params->value, string_required))
+                               optional = !required;
+                       else if (!strcmp(params->value, string_optional))
+                               optional = 1;
+                       else {
+                               ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
+                               errno = EPERM;
+                               return -1;
+                       }
+                       /* set the permission */
+                       if (request_permission(params->name)) {
+                               DEBUG("granted permission: %s", params->name);
+                       } else if (optional) {
+                               INFO("optional permission ungranted: %s", params->name);
+                       } else {
+                               ERROR("ungranted permission required: %s", params->name);
+                               errno = EPERM;
+                               return -1;
+                       }
                }
                params = params->next;
        }
@@ -144,6 +251,74 @@ static int check_permissions(const struct wgt_desc *desc)
        return result;
 }
 
+static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
+{
+       int rc, rc2;
+       struct wgt_desc_feature *feat;
+       const char *src, *type;
+
+       rc = action(desc->content_src, desc->content_type);
+       feat = desc->features;
+       while (feat) {
+               if (!strcmp(feat->name, "urn:AGL:widget:provided-unit")) {
+                       src = wgt_info_param(feat, "content.src");
+                       type = wgt_info_param(feat, "content.type");
+                       rc2 = action(src, type);
+                       if (rc >= 0 && rc2 < 0)
+                               rc = rc2;
+               }
+               feat = feat->next;
+       }
+       return rc;
+}
+
+static int set_exec_flag(const char *src, const char *type)
+{
+       int i, rc;
+
+       if (src && type) {
+               i = sizeof exec_type_strings / sizeof *exec_type_strings;
+               while (i) {
+                       if (!strcasecmp(type, exec_type_strings[--i])) {
+                               rc = fchmodat(workdirfd, src, 0755, 0);
+                               if (rc < 0)
+                                       ERROR("can't make executable the file %s", src);
+                               return rc;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int check_one_content(const char *src, const char *type)
+{
+       int rc;
+       struct stat s;
+
+       if (!src) {
+               ERROR("a content src is missing");
+               errno = EINVAL;
+               rc = -1;
+       } else {
+               /* TODO: when dealing with HTML and languages, the check should
+                * include i18n path search of widgets */
+               rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
+               if (rc < 0)
+                       ERROR("can't get info on content %s: %m", src);
+               else if (!S_ISREG(s.st_mode)) {
+                       ERROR("content %s isn't a regular file", src);
+                       errno = EINVAL;
+                       rc = -1;
+               }
+       }
+       return rc;
+}
+
+static int check_content(const struct wgt_desc *desc)
+{
+       return for_all_content(desc, check_one_content);
+}
+
 static int check_widget(const struct wgt_desc *desc)
 {
        int result;
@@ -151,6 +326,8 @@ static int check_widget(const struct wgt_desc *desc)
        result = check_temporary_constraints(desc);
        if (result >= 0)
                result = check_permissions(desc);
+       if (result >= 0)
+               result = check_content(desc);
        return result;
 }
 
@@ -204,16 +381,7 @@ static int install_icon(const struct wgt_desc *desc)
 
 static int install_exec_flag(const struct wgt_desc *desc)
 {
-       int i;
-
-       if (desc->content_type) {
-               i = sizeof exec_type_strings / sizeof *exec_type_strings;
-               while (i) {
-                       if (!strcasecmp(desc->content_type, exec_type_strings[--i]))
-                               return fchmodat(workdirfd, desc->content_src, 0755, 0);
-               }
-       }
-       return 0;
+       return for_all_content(desc, set_exec_flag);
 }
 
 static int install_security(const struct wgt_desc *desc)
@@ -288,6 +456,8 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
        struct wgt_info *ifo;
        const struct wgt_desc *desc;
        char installdir[PATH_MAX];
+       int port;
+       struct unitconf uconf;
 
        NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
 
@@ -322,15 +492,28 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
        if (install_icon(desc))
                goto error3;
 
+       if (install_security(desc))
+               goto error4;
+
        if (install_exec_flag(desc))
-               goto error3;
+               goto error4;
 
-       if (install_security(desc))
-               goto error3;
+       port = get_port();
+       if (port < 0)
+               goto error4;
+
+       uconf.installdir = installdir;
+       uconf.icondir = FWK_ICON_DIR;
+       uconf.port = port;
+       if (unit_install(ifo, &uconf))
+               goto error4;
 
        file_reset();
        return ifo;
 
+error4:
+       /* TODO: cleanup */
+
 error3:
        wgt_info_unref(ifo);