/*
- Copyright 2015 IoT.bzh
+ Copyright 2015, 2016, 2017 IoT.bzh
author: José Bollo <jose.bollo@iot.bzh>
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <poll.h>
+#include <signal.h>
extern char **environ;
#include "verbose.h"
+#include "afm-launch-mode.h"
#include "afm-launch.h"
#include "secmgr-wrap.h"
-/*
-%I icondir FWK_ICON_DIR
-%P port params->port
-%S secret params->secret
-%D datadir params->datadir
-%r rootdir desc->path
-%h homedir desc->home
-%t tag (smack label) desc->tag
-%a appid desc->appid
-%c content desc->content
-%m mime-type desc->type
-%n name desc->name
-%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
-};
+#define DEFAULT_TYPE "text/html"
-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
+/*
+ * structure for a launching type
+ */
+struct type_list {
+ struct type_list *next; /* next type */
+ char type[1]; /* type name */
};
-struct execdesc {
- const char *type;
- const char **master_args;
- const char **slave_args;
+/*
+ * structure for launch vectors
+ */
+struct exec_vector {
+ int has_readyfd; /* has a request for readyness */
+ const char **args; /* vector of arguments */
};
-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 desc_launcher {
+ struct desc_launcher *next; /* next launcher description */
+ enum afm_launch_mode mode; /* the launch mode */
+ struct type_list *types; /* the launched types */
+ struct exec_vector execs[2]; /* the launching vectors */
};
struct launchparam {
int port;
+ int readyfd;
+ char **uri;
const char *secret;
const char *datadir;
- const char **master_args;
- const char **slave_args;
+ struct exec_vector *execs;
};
+/*
+ * Structure for reading the configuration file
+ */
+struct confread {
+ const char *filepath; /* path of the configuration file */
+ FILE *file; /* handle to the file */
+ int lineno; /* current line number */
+ int index; /* start of the current token (in buffer) */
+ int length; /* length of the current token */
+ char buffer[4096]; /* current line */
+};
+
+/*
+ * list of launch descriptions
+ */
+struct desc_launcher *launchers = NULL;
+
+/*
+ * the group when launched (to avoid the setgid effect)
+ */
static gid_t groupid = 0;
-static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params)
+/*
+ * separators within configuration files
+ */
+static const char separators[] = " \t\n";
+
+/*
+ * default string emitted when for application not having signal
+ */
+static const char readystr[] = "READY=1";
+
+/*
+ * default time out for readiness of signaling applications
+ */
+static const int ready_timeout = 1500;
+
+#if defined(DUMP_LAUNCHERS)
+/*
+ * dump all the known launchers to the 'file'
+ */
+static void dump_launchers(FILE *file)
{
- const char **iter, *p, *v;
- char *data, **result, port[20], width[20], height[20], mini[3], c;
- int n, s;
+ int j, k;
+ struct desc_launcher *desc;
+ struct type_list *type;
+
+ for (desc = launchers ; desc != NULL ; desc = desc->next) {
+ fprintf(file, "mode %s\n", name_of_launch_mode(desc->mode));
+ for (type = desc->types ; type != NULL ; type = type->next)
+ fprintf(file, "%s\n", type->type);
+ for ( j = 0 ; j < 2 ; j++)
+ if (desc->execs[j].args != NULL) {
+ for (k = 0; desc->execs[j].args[k] != NULL; k++)
+ fprintf(file, " %s",
+ desc->execs[j].args[k]);
+ fprintf(file, "\n");
+ }
+ fprintf(file, "\n");
+ }
+}
+#endif
+
+/*
+ * update 'cread' to point the the next token
+ * returns the length of the token that is nul if no
+ * more token exists in the line
+ */
+static int next_token(struct confread *cread)
+{
+ int idx = cread->index + cread->length;
+ cread->index = idx + (int)strspn(&cread->buffer[idx], separators);
+ cread->length = (int)strcspn(&cread->buffer[cread->index], separators);
+ return cread->length;
+}
+
+/*
+ * reads the next line, skipping empty lines or lines
+ * having only comments.
+ * returns either 0 at end of the file, -1 in case of error or
+ * in case of success the length of the first token.
+ */
+static int read_line(struct confread *cread)
+{
+ while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
+ cread->lineno++;
+ cread->index = (int)strspn(cread->buffer, separators);
+ if (cread->buffer[cread->index]
+ && cread->buffer[cread->index] != '#') {
+ cread->length = (int)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;
+}
+
+/*
+ * extract from 'cread' a launch vector that is allocated in
+ * one piece of memory.
+ * 'cread' is left unchanged (index and length are not changed)
+ */
+static const char **read_vector(struct confread *cread)
+{
+ int index0, length0;
+ const char **vector;
+ char *args;
+ unsigned count, length;
+
+ /* record origin */
+ index0 = cread->index;
+ length0 = cread->length;
+
+ /* count */
+ count = 0;
+ length = 0;
+ while(cread->length) {
+ count++;
+ length += (unsigned)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],
+ (unsigned)cread->length);
+ args += cread->length;
+ *args++ = 0;
+ next_token(cread);
+ }
+ vector[count] = NULL;
+ cread->index = index0;
+ cread->length = length0;
+ return vector;
+}
+
+/*
+ * Reads the type from 'cread' directly in the list item and return it.
+ * returns NULL in case or error.
+ * errno:
+ * - EINVAL extra characters
+ * - ENOMEM memory depletion
+ */
+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) + (unsigned)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], (unsigned)length);
+ result->type[length] = 0;
+ return result;
+}
+
+/*
+ * Reads the mode from 'cread' and return it.
+ * returns invalid_launch_mode in case or error.
+ * errno:
+ * - EINVAL no mode or extra characters or invalid mode
+ */
+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_name(&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;
+}
+
+/*
+ * free the memory used by 'types'
+ */
+static void free_type_list(struct type_list *types)
+{
+ while (types != NULL) {
+ struct type_list *next = types->next;
+ free(types);
+ types = next;
+ }
+}
+
+/*
+ * reads the configuration file handled by 'cread'
+ * and adds its contents to the launcher list
+ *
+ * returns 0 in case of success or -1 in case of error.
+ */
+static int read_launchers(struct confread *cread)
+{
+ int rc, has_readyfd;
+ struct type_list *types, *lt;
+ struct desc_launcher *desc;
+ enum afm_launch_mode mode;
+ const 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"
+ " (the maximum count is 2)",
+ cread->filepath, cread->lineno);
+ errno = EINVAL;
+ return -1;
+ } else {
+ has_readyfd = NULL != strstr(
+ &cread->buffer[cread->index], "%R");
+ 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].has_readyfd = has_readyfd;
+ desc->execs[0].args = vector;
+ desc->execs[1].has_readyfd = 0;
+ desc->execs[1].args = NULL;
+ types = NULL;
+ launchers = desc;
+ } else {
+ desc->execs[1].has_readyfd = has_readyfd;
+ desc->execs[1].args = 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;
+}
+
+/*
+ * reads the configuration file 'filepath'
+ * and adds its contents to the launcher list
+ *
+ * returns 0 in case of success or -1 in case of error.
+ */
+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;
+}
+
+/*
+ * Creates a secret in 'buffer'
+ */
+static void mksecret(char buffer[9])
+{
+ snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
+}
+
+/*
+ * Allocates a port and return it.
+ */
+static int mkport()
+{
+ static int port_ring = 12345;
+ int port = port_ring;
+ if (port < 12345 || port > 15432)
+ port = 12345;
+ port_ring = port + 1;
+ return port;
+}
+
+/*
+%% %
+%a appid desc->appid
+%b bindings desc->bindings
+%c content desc->content
+%D datadir params->datadir
+%H height desc->height
+%h homedir desc->home
+%I icondir FWK_ICON_DIR
+%m mime-type desc->type
+%n name desc->name
+%P port params->port
+%r rootdir desc->path
+%R readyfd params->readyfd
+%S secret params->secret
+%W width desc->width
+*/
+
+/*
+ * Union for handling either scalar arguments or vectorial arguments.
+ */
+union arguments {
+ char *scalar; /* string of space separated arguments */
+ char **vector; /* vector of arguments */
+};
+
+/*
+ * Computes the substitutions of 'args' according to the
+ * data of 'desc' and 'params'. The result is a single
+ * piece of memory (that must be freed in one time)
+ * containing either a vector or a string depending on
+ * the value of 'wants_vector'.
+ *
+ * The vectors are made of an array pointers terminated by
+ * the NULL pointer.
+ *
+ * Returns the resulting value or NULL in case of error
+ */
+static union arguments instantiate_arguments(
+ const char * const *args,
+ struct afm_launch_desc *desc,
+ struct launchparam *params,
+ int wants_vector
+)
+{
+ const char * const *iter;
+ const char *p, *v;
+ char *data, c, sep;
+ unsigned n, s;
+ union arguments result;
+ char port[20], width[20], height[20], readyfd[20], mini[3];
/* init */
+ sep = wants_vector ? 0 : ' ';
mini[0] = '%';
mini[2] = 0;
- /* loop that either compute the size and build the result */
- data = NULL;
+ /*
+ * loop that either compute the size and build the result
+ * advantage: appears only in one place
+ */
+ result.vector = NULL; /* initialise both to avoid */
+ result.scalar = NULL; /* a very stupid compiler warning */
+ data = NULL; /* no data for the first iteration */
n = s = 0;
for (;;) {
- iter = args;
- n = 0;
- while (*iter) {
- p = *iter++;
- if (data)
- result[n] = data;
+ /* iterate over arguments */
+ for (n = 0, iter = args ; (p = *iter) != NULL ; iter++) {
+ /* init the vector */
+ if (data && !sep)
+ result.vector[n] = data;
n++;
+
+ /* scan the argument */
while((c = *p++) != 0) {
if (c != '%') {
+ /* standard character */
if (data)
*data++ = c;
else
s++;
} else {
+ /* substitutions */
+ /* (convert num->string only once) */
c = *p++;
switch (c) {
- case 'I': v = FWK_ICON_DIR; break;
- case 'P': if(!data) sprintf(port, "%d", params->port); v = port; break;
- case 'S': v = params->secret; break;
- case 'D': v = params->datadir; break;
- case 'r': v = desc->path; break;
- case 'h': v = desc->home; break;
- case 't': v = desc->tag; break;
case 'a': v = desc->appid; break;
+ case 'b':
+ v = "" /*TODO:desc->bindings*/;
+ break;
case 'c': v = desc->content; break;
+ case 'D': v = params->datadir; break;
+ case 'H':
+ if(!data)
+ sprintf(height, "%d",
+ desc->height);
+ v = height;
+ break;
+ case 'h': v = desc->home; break;
+ case 'I': v = FWK_ICON_DIR; break;
case 'm': v = desc->type; break;
case 'n': v = desc->name; break;
- case 'p': v = "" /*desc->plugins*/; break;
- case 'W': if(!data) sprintf(width, "%d", desc->width); v = width; break;
- case 'H': if(!data) sprintf(height, "%d", desc->height); v = height; break;
- case '%': c = 0;
- default: mini[1] = c; v = mini; break;
+ case 'P':
+ if(!data)
+ sprintf(port, "%d",
+ params->port);
+ v = port;
+ break;
+ case 'R':
+ if(!data)
+ sprintf(readyfd, "%d",
+ params->readyfd);
+ v = readyfd;
+ break;
+ case 'r': v = desc->path; break;
+ case 'S': v = params->secret; break;
+ case 'W':
+ if(!data)
+ sprintf(width, "%d",
+ desc->width);
+ v = width;
+ break;
+ case '%':
+ c = 0;
+ default:
+ mini[1] = c;
+ v = mini;
+ break;
}
if (data)
data = stpcpy(data, v);
else
- s += strlen(v);
+ s += (unsigned)strlen(v);
}
}
+ /* terminate the argument */
if (data)
- *data++ = 0;
+ *data++ = sep;
else
s++;
}
- if (data) {
- result[n] = NULL;
+ if (!data) {
+ /* first iteration: allocation */
+ if (sep) {
+ result.scalar = malloc(s);
+ data = result.scalar;
+ } else {
+ result.vector = malloc((n+1)*sizeof(char*) + s);
+ if (result.vector != NULL)
+ data = (char*)(&result.vector[n + 1]);
+ }
+ if (!data) {
+ errno = ENOMEM;
+ return result;
+ }
+ } else {
+ /* second iteration: termination */
+ if (sep)
+ *--data = 0;
+ else
+ result.vector[n] = NULL;
return result;
}
- /* allocation */
- result = malloc((n+1)*sizeof(char*) + s);
- if (result == NULL) {
- errno = ENOMEM;
- return NULL;
- }
- data = (char*)(&result[n + 1]);
}
}
-static void mksecret(char buffer[9])
+/*
+ * Launchs (fork-execs) the program described by 'exec'
+ * using the parameters of 'desc' and 'params' to instantiate
+ * it. The created process is attached to the process group 'progrp'.
+ *
+ * After being created and before to be launched, the process
+ * is put in its security environment and its directory is
+ * changed to params->datadir.
+ *
+ * Returns the pid of the created process or -1 in case of error.
+ */
+static pid_t launch(
+ struct afm_launch_desc *desc,
+ struct launchparam *params,
+ struct exec_vector *exec,
+ pid_t progrp
+)
{
- snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
-}
+ int rc;
+ char **args, **env;
+ pid_t pid;
+ int rpipe[2];
+ struct pollfd pfd;
-static int mkport()
-{
- static int port_ring = 12345;
- int port = port_ring;
- if (port < 12345 || port > 15432)
- port = 12345;
- port_ring = port + 1;
- return port;
-}
+ /* prepare the pipes */
+ rc = pipe(rpipe);
+ if (rc < 0) {
+ ERROR("error while calling pipe2: %m");
+ return -1;
+ }
+ /* instanciate the arguments */
+ params->readyfd = rpipe[1];
+ args = instantiate_arguments(exec->args, desc, params, 1).vector;
+ env = instantiate_arguments((const char * const*)environ,
+ desc, params, 1).vector;
+ if (args == NULL || env == NULL) {
+ close(rpipe[0]);
+ close(rpipe[1]);
+ free(args);
+ free(env);
+ ERROR("out of memory in master");
+ errno = ENOMEM;
+ return -1;
+ }
+ /* fork the master child */
+ pid = fork();
+ if (pid < 0) {
-static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
-{
- int rc;
- char **args;
+ /********* can't fork ************/
- /* fork the master child */
- children[0] = fork();
- if (children[0] < 0) {
+ close(rpipe[0]);
+ close(rpipe[1]);
+ free(args);
+ free(env);
ERROR("master fork failed: %m");
return -1;
}
- if (children[0]) {
+ if (pid) {
+
/********* in the parent process ************/
- return 0;
+
+ close(rpipe[1]);
+ free(args);
+ free(env);
+ pfd.fd = rpipe[0];
+ pfd.events = POLLIN;
+
+ /* wait for readyness */
+ poll(&pfd, 1, ready_timeout);
+ close(rpipe[0]);
+ return pid;
}
- /********* in the master child ************/
+ /********* in the child process ************/
+
+ close(rpipe[0]);
+
+ /* set name by appid */
+ verbose_set_name(desc->appid, 0);
/* avoid set-gid effect */
setresgid(groupid, groupid, groupid);
/* enter the process group */
- rc = setpgid(0, 0);
+ rc = setpgid(0, progrp);
if (rc) {
ERROR("setpgid failed");
_exit(1);
}
/* enter security mode */
- rc = secmgr_prepare_exec(desc->tag);
+ rc = secmgr_prepare_exec(desc->appid);
if (rc < 0) {
ERROR("call to secmgr_prepare_exec failed: %m");
_exit(1);
_exit(1);
}
- args = instantiate_arguments(params->master_args, desc, params);
- if (args == NULL) {
- ERROR("out of memory in master");
- }
- else {
- rc = execve(args[0], args, environ);
- ERROR("failed to exec master %s: %m", args[0]);
+ /* signal if needed */
+ if (!exec->has_readyfd) {
+ write(rpipe[1], readystr, sizeof(readystr) - 1);
+ close(rpipe[1]);
}
+
+ /* executes the process */
+ rc = execve(args[0], args, env);
+ access(args[0], X_OK);
+ ERROR("failed to exec master %s: %m", args[0]);
_exit(1);
+ return -1;
}
-static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
+/*
+ * Launches the application 'desc' in local mode using
+ * 'params' and store the resulting pids in 'children'.
+ *
+ * Returns 0 in case of success or -1 in case of error.
+ */
+static int launch_local(
+ struct afm_launch_desc *desc,
+ pid_t children[2],
+ struct launchparam *params
+)
{
- int rc;
- char message[10];
- int mpipe[2];
- int spipe[2];
- char **args;
-
- /* prepare the pipes */
- rc = pipe2(mpipe, O_CLOEXEC);
- if (rc < 0) {
- ERROR("error while calling pipe2: %m");
- return -1;
- }
- rc = pipe2(spipe, O_CLOEXEC);
- if (rc < 0) {
- ERROR("error while calling pipe2: %m");
- close(spipe[0]);
- close(spipe[1]);
+ /* launches the first, making it group leader */
+ children[0] = launch(desc, params, ¶ms->execs[0], 0);
+ if (children[0] <= 0)
return -1;
- }
- /* fork the master child */
- children[0] = fork();
- if (children[0] < 0) {
- ERROR("master fork failed: %m");
- close(mpipe[0]);
- close(mpipe[1]);
- close(spipe[0]);
- close(spipe[1]);
- return -1;
- }
- if (children[0]) {
- /********* in the parent process ************/
- close(mpipe[1]);
- close(spipe[0]);
- /* wait the ready signal (that transmit the slave pid) */
- rc = read(mpipe[0], &children[1], sizeof children[1]);
- close(mpipe[0]);
- if (rc <= 0) {
- ERROR("reading master pipe failed: %m");
- close(spipe[1]);
- return -1;
- }
- assert(rc == sizeof children[1]);
- /* start the child */
- rc = write(spipe[1], "start", 5);
- if (rc < 0) {
- ERROR("writing slave pipe failed: %m");
- close(spipe[1]);
- return -1;
- }
- assert(rc == 5);
- close(spipe[1]);
+ /* nothing more to launch ? */
+ if (params->execs[1].args == NULL)
return 0;
- }
-
- /********* in the master child ************/
- close(mpipe[0]);
- close(spipe[1]);
- /* avoid set-gid effect */
- setresgid(groupid, groupid, groupid);
-
- /* enter the process group */
- rc = setpgid(0, 0);
- if (rc) {
- ERROR("setpgid failed");
- _exit(1);
- }
-
- /* enter security mode */
- rc = secmgr_prepare_exec(desc->tag);
- if (rc < 0) {
- ERROR("call to secmgr_prepare_exec failed: %m");
- _exit(1);
- }
+ /* launches the second in the group of the first */
+ children[1] = launch(desc, params, ¶ms->execs[1], children[0]);
+ if (children[1] > 0)
+ return 0;
- /* enter the datadirectory */
- rc = mkdir(params->datadir, 0755);
- if (rc && errno != EEXIST) {
- ERROR("creation of datadir %s failed: %m", params->datadir);
- _exit(1);
- }
- rc = chdir(params->datadir);
- if (rc) {
- ERROR("can't enter the datadir %s: %m", params->datadir);
- _exit(1);
- }
+ /* kill all on error */
+ killpg(children[0], SIGKILL);
+ return -1;
+}
- /* fork the slave child */
- children[1] = fork();
- if (children[1] < 0) {
- ERROR("slave fork failed: %m");
- _exit(1);
+/*
+ * Launches the application 'desc' in remote mode using
+ * 'params' and store the resulting pids in 'children'.
+ *
+ * Returns 0 in case of success or -1 in case of error.
+ */
+static int launch_remote(
+ struct afm_launch_desc *desc,
+ pid_t children[2],
+ struct launchparam *params
+)
+{
+ char *uri;
+
+ /* instanciate the uri */
+ if (params->execs[1].args == NULL)
+ uri = NULL;
+ else
+ uri = instantiate_arguments(params->execs[1].args, desc,
+ params, 0).scalar;
+ if (uri == NULL) {
+ ERROR("out of memory for remote uri");
+ errno = ENOMEM;
+ return -1;
}
- if (children[1] == 0) {
- /********* in the slave child ************/
- close(mpipe[0]);
- rc = read(spipe[0], message, sizeof message);
- if (rc <= 0) {
- ERROR("reading slave pipe failed: %m");
- _exit(1);
- }
- args = instantiate_arguments(params->slave_args, desc, params);
- if (args == NULL) {
- ERROR("out of memory in slave");
- }
- else {
- rc = execve(args[0], args, environ);
- ERROR("failed to exec slave %s: %m", args[0]);
- }
- _exit(1);
+ /* launch the command */
+ children[0] = launch(desc, params, ¶ms->execs[0], 0);
+ if (children[0] <= 0) {
+ free(uri);
+ return -1;
}
- /********* still in the master child ************/
- close(spipe[1]);
- args = instantiate_arguments(params->master_args, desc, params);
- if (args == NULL) {
- ERROR("out of memory in master");
- }
- else {
- rc = write(mpipe[1], &children[1], sizeof children[1]);
- if (rc <= 0) {
- ERROR("can't write master pipe: %m");
- }
- else {
- close(mpipe[1]);
- rc = execve(args[0], args, environ);
- ERROR("failed to exec master %s: %m", args[0]);
- }
- }
- _exit(1);
+ /* returns the uri in params */
+ *params->uri = uri;
+ return 0;
}
-static void afm_launch_init_group()
+/*
+ * Searchs the launcher descritpion for the given 'type' and 'mode'
+ *
+ * Returns the description found or NULL if nothing matches.
+ */
+static struct desc_launcher *search_launcher(const char *type,
+ enum afm_launch_mode mode)
{
- if (!groupid) {
- gid_t r, e, s;
- getresgid(&r, &e, &s);
- if (s && s != e)
- groupid = s;
- else
- groupid = -1;
- }
+ struct desc_launcher *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 (!strcasecmp(tl->type, type))
+ return dl;
+ return NULL;
}
-int afm_launch(struct afm_launch_desc *desc, pid_t children[2])
+/*
+ * Launches the application described by 'desc'
+ * and, in case of success, returns the resulting data
+ * in 'children' and 'uri'.
+ *
+ * Returns 0 in case of success or -1 in case of error.
+ */
+int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
{
+ int rc;
char datadir[PATH_MAX];
- int ikl, nkl, rc;
char secret[9];
struct launchparam params;
+ const char *type;
+ struct desc_launcher *dl;
+
+ /* should be init */
+ assert(groupid != 0);
+ assert(is_valid_launch_mode(desc->mode));
+ assert(desc->mode == mode_local || uri != NULL);
+ assert(uri == NULL || *uri == NULL);
- /* static init */
- afm_launch_init_group();
+ /* 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("launcher not found for type %s and mode %s!",
+ type, name_of_launch_mode(desc->mode));
+ errno = ENOENT;
+ return -1;
}
/* prepare paths */
- rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
- if (rc < 0 || rc >= sizeof datadir) {
+ rc = snprintf(datadir, sizeof datadir, "%s/%s",
+ desc->home, desc->appid);
+ if (rc < 0 || rc >= (int)sizeof datadir) {
ERROR("overflow for datadir");
errno = EINVAL;
return -1;
/* make the secret and port */
mksecret(secret);
+ params.uri = uri;
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.execs = dl->execs;
+
+ switch (desc->mode) {
+ case mode_local:
+ return launch_local(desc, children, ¶ms);
+ case mode_remote:
+ return launch_remote(desc, children, ¶ms);
+ default:
+ assert(0);
+ return -1;
+ }
+}
- return params.slave_args ? launchexec2(desc, children, ¶ms) : launchexec1(desc, children, ¶ms);
+/*
+ * Initialise the module
+ *
+ * Returns 0 on success or else -1 in case of failure
+ */
+int afm_launch_initialize()
+{
+ int rc;
+ gid_t r, e, s;
+
+ /* compute the groupid to set at launch */
+ getresgid(&r, &e, &s);
+ if (s && s != e)
+ groupid = s; /* the original groupid is used */
+ else
+ groupid = (gid_t)-1;
+
+ /* reads the configuration file */
+ rc = read_configuration_file(FWK_LAUNCH_CONF);
+#if defined(DUMP_LAUNCHERS)
+ if (!rc)
+ dump_launchers(stderr);
+#endif
+
+ return rc;
}