afm-launch: begins integration of modes
[src/app-framework-main.git] / src / afm-launch.c
index a66004b..87e8e3a 100644 (file)
 extern char **environ;
 
 #include "verbose.h"
+#include "afm-launch-mode.h"
 #include "afm-launch.h"
 #include "secmgr-wrap.h"
 
+#define DEFAULT_TYPE "text/html"
+
+struct type_list {
+       struct type_list *next;
+       char type[1];
+};
+
+struct desc_list {
+       struct desc_list *next;
+       enum afm_launch_mode mode;
+       struct type_list *types;
+       char **execs[2];
+};
+
+struct launchparam {
+       int port;
+       const char *secret;
+       const char *datadir;
+       const char **master;
+       const char **slave;
+};
+
+struct confread {
+       const char *filepath;
+       FILE *file;
+       int lineno;
+       int index;
+       int length;
+       char buffer[4096];
+};
+
+struct desc_list *launchers = NULL;
+
+static gid_t groupid = 0;
+
+const char separators[] = " \t\n";
+
+static void dump_launchers()
+{
+       int j, k;
+       struct desc_list *desc;
+       struct type_list *type;
+
+       for (desc = launchers ; desc != NULL ; desc = desc->next) {
+               printf("mode %s\n", name_of_launch_mode(desc->mode));
+               for (type = desc->types ; type != NULL ; type = type->next)
+                       printf("%s\n", type->type);
+               for ( j = 0 ; j < 2 ; j++)
+                       if (desc->execs[j] != NULL) {
+                               for (k = 0 ; desc->execs[j][k] != NULL ; k++)
+                                       printf("  %s", desc->execs[j][k]);
+                               printf("\n");
+                       }
+               printf("\n");
+       }
+}
+
+static int next_token(struct confread *cread)
+{
+       cread->index += cread->length + strspn(&cread->buffer[cread->index + cread->length], separators);
+       cread->length = strcspn(&cread->buffer[cread->index], separators);
+       return cread->length;
+}
+
+static int read_line(struct confread *cread)
+{
+       while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
+               cread->lineno++;
+               cread->index = strspn(cread->buffer, separators);
+               if (cread->buffer[cread->index] && cread->buffer[cread->index] != '#') {
+                       cread->length = strcspn(&cread->buffer[cread->index], separators);
+                       assert(cread->length > 0);
+                       return cread->length;
+               }
+       }
+       if (ferror(cread->file)) {
+               ERROR("%s:%d: error while reading, %m", cread->filepath, cread->lineno);
+               return -1;
+       }
+       return 0;
+}
+
+static char **read_vector(struct confread *cread)
+{
+       int index0, length0;
+       char **vector, *args;
+       int count, length;
+
+       /* record origin */
+       index0 = cread->index;
+       length0 = cread->length;
+
+       /* count */
+       count = 0;
+       length = 0;
+       while(cread->length) {
+               count++;
+               length += cread->length;
+               next_token(cread);
+       }
+
+       /* allocates */
+       cread->index = index0;
+       cread->length = length0;
+       vector = malloc(length + count + (count + 1) * sizeof(char*));
+       if (vector == NULL)
+               return NULL;
+
+       /* copies */
+       args = (char*)(vector + count + 1);
+       count = 0;
+       while(cread->length) {
+               vector[count++] = args;
+               memcpy(args, &cread->buffer[cread->index], cread->length);
+               args += cread->length;
+               *args++ = 0;
+               next_token(cread);
+       }
+       vector[count] = NULL;
+       cread->index = index0;
+       cread->length = length0;
+       return vector;
+}
+
+static struct type_list *read_type(struct confread *cread)
+{
+       int index, length;
+       struct type_list *result;
+
+       /* record index and length */
+       index = cread->index;
+       length = cread->length;
+
+       /* check no extra characters */
+       if (next_token(cread)) {
+               ERROR("%s:%d: extra characters found after type %.*s", cread->filepath, cread->lineno, length, &cread->buffer[index]);
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /* allocate structure */
+       result = malloc(sizeof(struct type_list) + length);
+       if (result == NULL) {
+               ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       /* fill the structure */
+       memcpy(result->type, &cread->buffer[index], length);
+       result->type[length] = 0;
+       return result;
+}
+
+static enum afm_launch_mode read_mode(struct confread *cread)
+{
+       int index, length;
+       enum afm_launch_mode result;
+
+       assert(cread->index == 0);
+       assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
+
+       /* get the next token: the mode string */
+       if (!next_token(cread)) {
+               ERROR("%s:%d: no mode value set", cread->filepath, cread->lineno);
+               errno = EINVAL;
+               return invalid_launch_mode;
+       }
+
+       /* record index and length */
+       index = cread->index;
+       length = cread->length;
+
+       /* check no extra characters */
+       if (next_token(cread)) {
+               ERROR("%s:%d: extra characters found after mode %.*s", cread->filepath, cread->lineno, length, &cread->buffer[index]);
+               errno = EINVAL;
+               return invalid_launch_mode;
+       }
+
+       /* get the mode */
+       cread->buffer[index + length] = 0;
+       result = launch_mode_of_string(&cread->buffer[index]);
+       if (result == invalid_launch_mode) {
+               ERROR("%s:%d: invalid mode value %s", cread->filepath, cread->lineno, &cread->buffer[index]);
+               errno = EINVAL;
+       }
+       return result;
+}
+
+static void free_type_list(struct type_list *types)
+{
+       while (types != NULL) {
+               struct type_list *next = types->next;
+               free(types);
+               types = next;
+       }
+}
+
+static int read_launchers(struct confread *cread)
+{
+       int rc;
+       struct type_list *types, *lt;
+       struct desc_list *desc;
+       enum afm_launch_mode mode;
+       char **vector;
+
+       /* reads the file */
+       lt = NULL;
+       types = NULL;
+       desc = NULL;
+       mode = invalid_launch_mode;
+       rc = read_line(cread);
+       while (rc > 0) {
+               if (cread->index == 0) {
+                       if (cread->length == 4 && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
+                               /* check if allowed */
+                               if (types != NULL) {
+                                       ERROR("%s:%d: mode found before launch vector", cread->filepath, cread->lineno);
+                                       errno = EINVAL;
+                                       free_type_list(types);
+                                       return -1;
+                               }
+
+                               /* read the mode */
+                               mode = read_mode(cread);
+                               if (mode == invalid_launch_mode)
+                                       return -1;
+                       } else {
+                               if (mode == invalid_launch_mode) {
+                                       ERROR("%s:%d: mode not found before type", cread->filepath, cread->lineno);
+                                       errno = EINVAL;
+                                       assert(types == NULL);
+                                       return -1;
+                               }
+                               /* read a type */
+                               lt = read_type(cread);
+                               if (lt == NULL) {
+                                       free_type_list(types);
+                                       return -1;
+                               }
+                               lt->next = types;
+                               types = lt;
+                       }
+                       desc = NULL;
+               } else if (types == NULL && desc == NULL) {
+                       if (lt == NULL)
+                               ERROR("%s:%d: untyped launch vector found", cread->filepath, cread->lineno);
+                       else
+                               ERROR("%s:%d: extra launch vector found (2 max)", cread->filepath, cread->lineno);
+                       errno = EINVAL;
+                       return -1;
+               } else {
+                       vector = read_vector(cread);
+                       if (vector == NULL) {
+                               ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
+                               free_type_list(types);
+                               errno = ENOMEM;
+                               return -1;
+                       }
+                       if (types) {
+                               assert(desc == NULL);
+                               desc = malloc(sizeof * desc);
+                               if (desc == NULL) {
+                                       ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
+                                       free_type_list(types);
+                                       errno = ENOMEM;
+                                       return -1;
+                               }
+                               desc->next = launchers;
+                               desc->mode = mode;
+                               desc->types = types;
+                               desc->execs[0] = vector;
+                               desc->execs[1] = NULL;
+                               types = NULL;
+                               launchers = desc;
+                       } else {
+                               desc->execs[1] = vector;
+                               desc = NULL;
+                       }
+               }
+               rc = read_line(cread);
+       }
+       if (types != NULL) {
+               ERROR("%s:%d: end of file found before launch vector", cread->filepath, cread->lineno);
+               free_type_list(types);
+               errno = EINVAL;
+               return -1;
+       }
+       return rc;
+}
+
+static int read_configuration_file(const char *filepath)
+{
+       int rc;
+       struct confread cread;
+
+       /* opens the configuration file */
+       cread.file = fopen(filepath, "r");
+       if (cread.file == NULL) {
+               /* error */
+               ERROR("can't read file %s: %m", filepath);
+               rc = -1;
+       } else {
+               /* reads it */
+               cread.filepath = filepath;
+               cread.lineno = 0;
+               rc = read_launchers(&cread);
+               fclose(cread.file);
+       }
+       return rc;
+}
+
 /*
 %I icondir                     FWK_ICON_DIR
 %P port                                params->port
@@ -50,60 +364,9 @@ extern char **environ;
 %p plugins                     desc->plugins
 %W width                       desc->width
 %H height                      desc->height
+%% %
 */
 
-static const char *args_for_afb_daemon[] = {
-       "/usr/bin/afb-daemon",
-       "--daemon",
-       "--alias=/icons:%I",
-       "--port=%P",
-       "--rootdir=%D",
-       "--token=%S",
-       NULL
-};
-
-static const char *args_for_qmlviewer[] = {
-       "/usr/bin/qt5/qmlscene",
-       "-fullscreen",
-       "-I",
-       "%r",
-       "-I",
-       "%r/imports",
-       "%r/%c",
-       NULL
-};
-
-static const char *args_for_web_runtime[] = {
-       "/usr/bin/web-runtime",
-       "http://localhost:%P/%c?token=%S",
-       NULL
-};
-
-static const char *args_for_binary[] = {
-       "%r/%c",
-       NULL
-};
-
-struct execdesc {
-       const char *type;
-       const char **master_args;
-       const char **slave_args;
-};
-
-static struct execdesc known_launchers[] = {
-       { "text/html",                args_for_afb_daemon, args_for_web_runtime },
-       { "application/x-executable", args_for_binary,     NULL },
-       { "text/vnd.qt.qml",          args_for_qmlviewer,  NULL }
-};
-
-struct launchparam {
-       int port;
-       const char *secret;
-       const char *datadir;
-       const char **master_args;
-       const char **slave_args;
-};
-
 static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params)
 {
        const char **iter, *p, *v;
@@ -116,6 +379,7 @@ static char **instantiate_arguments(const char **args, struct afm_launch_desc *d
 
        /* loop that either compute the size and build the result */
        data = NULL;
+       result = NULL;
        n = s = 0;
        for (;;) {
                iter = args;
@@ -191,8 +455,6 @@ static int mkport()
        return port;
 }
 
-
-
 static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
 {
        int rc;
@@ -210,6 +472,10 @@ static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct l
        }
 
        /********* in the master child ************/
+
+       /* avoid set-gid effect */
+       setresgid(groupid, groupid, groupid);
+
        /* enter the process group */
        rc = setpgid(0, 0);
        if (rc) {
@@ -236,7 +502,7 @@ static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct l
                _exit(1);
        }
 
-       args = instantiate_arguments(params->master_args, desc, params);
+       args = instantiate_arguments(params->master, desc, params);
        if (args == NULL) {
                ERROR("out of memory in master");
        }
@@ -308,6 +574,9 @@ static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct l
        close(mpipe[0]);
        close(spipe[1]);
 
+       /* avoid set-gid effect */
+       setresgid(groupid, groupid, groupid);
+
        /* enter the process group */
        rc = setpgid(0, 0);
        if (rc) {
@@ -349,7 +618,7 @@ static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct l
                        _exit(1);
                }
 
-               args = instantiate_arguments(params->slave_args, desc, params);
+               args = instantiate_arguments(params->slave, desc, params);
                if (args == NULL) {
                        ERROR("out of memory in slave");
                }
@@ -362,7 +631,7 @@ static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct l
 
        /********* still in the master child ************/
        close(spipe[1]);
-       args = instantiate_arguments(params->master_args, desc, params);
+       args = instantiate_arguments(params->master, desc, params);
        if (args == NULL) {
                ERROR("out of memory in master");
        }
@@ -380,24 +649,59 @@ static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct l
        _exit(1);
 }
 
-int afm_launch(struct afm_launch_desc *desc, pid_t children[2])
+int afm_launch_initialize()
+{
+       int rc;
+       gid_t r, e, s;
+
+       getresgid(&r, &e, &s);
+       if (s && s != e)
+               groupid = s;
+       else
+               groupid = -1;
+
+       rc = read_configuration_file(FWK_LAUNCH_CONF);
+       dump_launchers();
+       return rc;
+}
+
+static struct desc_list *search_launcher(const char *type, enum afm_launch_mode mode)
+{
+       struct desc_list *dl;
+       struct type_list *tl;
+
+       for (dl = launchers ; dl ; dl = dl->next)
+               if (dl->mode == mode)
+                       for (tl = dl->types ; tl != NULL ; tl = tl->next)
+                               if (!strcmp(tl->type, type))
+                                       return dl;
+       return NULL;
+}
+
+int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
 {
        char datadir[PATH_MAX];
-       int ikl, nkl, rc;
+       int rc;
        char secret[9];
        struct launchparam params;
+       const char *type;
+       struct desc_list *dl;
+
+       /* should be init */
+       assert(groupid != 0);
+       assert(launch_mode_is_valid(desc->mode));
+
+       /* init */
+       children[0] = 0;
+       children[1] = 0;
 
        /* what launcher ? */
-       ikl = 0;
-       if (desc->type != NULL && *desc->type) {
-               nkl = sizeof known_launchers / sizeof * known_launchers;
-               while (ikl < nkl && strcmp(desc->type, known_launchers[ikl].type))
-                       ikl++;
-               if (ikl == nkl) {
-                       ERROR("type %s not found!", desc->type);
-                       errno = ENOENT;
-                       return -1;
-               }
+       type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
+       dl = search_launcher(type, desc->mode);
+       if (dl == NULL) {
+               ERROR("type %s not found for mode %s!", type, name_of_launch_mode(desc->mode));
+               errno = ENOENT;
+               return -1;
        }
 
        /* prepare paths */
@@ -413,9 +717,9 @@ int afm_launch(struct afm_launch_desc *desc, pid_t children[2])
        params.port = mkport();
        params.secret = secret;
        params.datadir = datadir;
-       params.master_args = known_launchers[ikl].master_args;
-       params.slave_args = known_launchers[ikl].slave_args;
+       params.master = (const char **)dl->execs[0];
+       params.slave = (const char **)dl->execs[1];
 
-       return params.slave_args ? launchexec2(desc, children, &params) : launchexec1(desc, children, &params);
+       return params.slave ? launchexec2(desc, children, &params) : launchexec1(desc, children, &params);
 }