X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafm-launch.c;h=839194340389e7f8e2c1bb9489cfb59246c6f43e;hb=6f6d04fef9f08d756a37d17333f5b9b9a6b72dd2;hp=47f3f8b8cdd756aae53b751f9b9efe332434681b;hpb=89e45c0c97859b4f93095c28784bc2b2c723fc1b;p=src%2Fapp-framework-main.git diff --git a/src/afm-launch.c b/src/afm-launch.c index 47f3f8b..8391943 100644 --- a/src/afm-launch.c +++ b/src/afm-launch.c @@ -1,5 +1,5 @@ /* - Copyright 2015 IoT.bzh + Copyright 2015, 2016 IoT.bzh author: José Bollo @@ -28,98 +28,164 @@ #include #include #include +#include +#include extern char **environ; #include "verbose.h" +#include "afm-launch-mode.h" #include "afm-launch.h" #include "secmgr-wrap.h" #define DEFAULT_TYPE "text/html" -const char separators[] = " \t\n"; - -struct execdesc { - char *type; - char **execs[2]; +/* + * structure for a launching type + */ +struct type_list { + struct type_list *next; /* next type */ + char type[1]; /* type name */ }; -struct launchers { - int count; - struct execdesc *descs; +/* + * structure for launch vectors + */ +struct exec_vector { + int has_readyfd; /* has a request for readyness */ + const char **args; /* vector of arguments */ }; -static struct launchers launchers = { 0, 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; - const char **slave; + struct exec_vector *execs; }; -static gid_t groupid = 0; - +/* + * Structure for reading the configuration file + */ struct confread { - const char *filepath; - FILE *file; - int lineno; - int index; - int length; - char buffer[4096]; + 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 */ }; -static void dump_launchers(struct launchers *launchs) +/* + * list of launch descriptions + */ +struct desc_launcher *launchers = NULL; + +/* + * the group when launched (to avoid the setgid effect) + */ +static gid_t groupid = 0; + +/* + * 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) { - int i, j, k; - for (i = 0 ; i < launchs->count ; i++) { - printf("%s\n", launchs->descs[i].type); + 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 (launchs->descs[i].execs[j] != NULL) { - for (k = 0 ; launchs->descs[i].execs[j][k] != NULL ; k++) - printf(" %s", launchs->descs[i].execs[j][k]); - printf("\n"); + 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) { - cread->index += cread->length + strspn(&cread->buffer[cread->index + cread->length], separators); - cread->length = strcspn(&cread->buffer[cread->index], separators); + 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 = strspn(cread->buffer, separators); - if (cread->buffer[cread->index] && cread->buffer[cread->index] != '#') { - cread->length = strcspn(&cread->buffer[cread->index], separators); + 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); + ERROR("%s:%d: error while reading, %m", cread->filepath, + cread->lineno); return -1; } return 0; } -static char *dup_token(struct confread *cread) -{ - assert(cread->length); - return strndup(&cread->buffer[cread->index], cread->length); -} - -static char **dup_tokens_vector(struct confread *cread) +/* + * 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; - char **vector, *args; - int count, length; + const char **vector; + char *args; + unsigned count, length; /* record origin */ index0 = cread->index; @@ -130,7 +196,7 @@ static char **dup_tokens_vector(struct confread *cread) length = 0; while(cread->length) { count++; - length += cread->length; + length += (unsigned)cread->length; next_token(cread); } @@ -146,7 +212,8 @@ static char **dup_tokens_vector(struct confread *cread) count = 0; while(cread->length) { vector[count++] = args; - memcpy(args, &cread->buffer[cread->index], cread->length); + memcpy(args, &cread->buffer[cread->index], + (unsigned)cread->length); args += cread->length; *args++ = 0; next_token(cread); @@ -157,102 +224,226 @@ static char **dup_tokens_vector(struct confread *cread) return vector; } -static int read_type(struct confread *cread) +/* + * 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 count; - struct execdesc *descs; - char *type; + int index, length; + struct type_list *result; - /* get the type */ - type = dup_token(cread); - if (type == NULL) { - ERROR("%s:%d: out of memory", cread->filepath, cread->lineno); - errno = ENOMEM; - return -1; - } + /* record index and length */ + index = cread->index; + length = cread->length; - /* check the type */ + /* check no extra characters */ if (next_token(cread)) { - ERROR("%s:%d: extra characters found after type %s", cread->filepath, cread->lineno, type); - free(type); + ERROR("%s:%d: extra characters found after type %.*s", + cread->filepath, cread->lineno, length, + &cread->buffer[index]); errno = EINVAL; - return -1; + return NULL; } - /* allocates data */ - count = launchers.count + 1; - descs = realloc(launchers.descs, count * sizeof(struct execdesc)); - if (descs == NULL) { - free(type); + /* 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 -1; + return NULL; } - /* fill data */ - launchers.descs = descs; - descs += count - 1; - descs->type = type; - descs->execs[0] = NULL; - descs->execs[1] = NULL; - launchers.count = count; - return 0; + /* fill the structure */ + memcpy(result->type, &cread->buffer[index], (unsigned)length); + result->type[length] = 0; + return result; } -static int read_args(struct confread *cread, int bottom, int offset) +/* + * 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) { - char **vector; + int index, length; + enum afm_launch_mode result; - while (bottom < launchers.count) { - vector = dup_tokens_vector(cread); - if (vector == NULL) { - ERROR("%s:%d: out of memory", cread->filepath, cread->lineno); - return -1; - } - launchers.descs[bottom++].execs[offset] = vector; + 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; } - return 0; } +/* + * 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, bottom, offset, typed; + int rc, has_readyfd; + struct type_list *types, *lt; + struct desc_launcher *desc; + enum afm_launch_mode mode; + const char **vector; /* reads the file */ - offset = 0; - typed = 0; - bottom = launchers.count; + lt = NULL; + types = NULL; + desc = NULL; + mode = invalid_launch_mode; rc = read_line(cread); while (rc > 0) { if (cread->index == 0) { - if (!typed) - bottom = launchers.count; - rc = read_type(cread); - if (rc) - return rc; - if (!typed) { - typed = 1; - offset = 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; } - } else if (!typed && !offset) { - ERROR("%s:%d: untyped launcher found", cread->filepath, cread->lineno); - errno = EINVAL; - return -1; - } else if (offset >= 2) { - ERROR("%s:%d: extra launcher found", cread->filepath, cread->lineno); + 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 { - rc = read_args(cread, bottom, offset); - if (rc) - return rc; - offset++; - typed = 0; + 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; @@ -275,141 +466,283 @@ static int read_configuration_file(const char *filepath) } /* -%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 + * 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 plugins desc->plugins +%P port params->port +%r rootdir desc->path +%R readyfd params->readyfd +%S secret params->secret %W width desc->width -%H height desc->height -%% % */ -static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params) +/* + * 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 **iter, *p, *v; - char *data, **result, port[20], width[20], height[20], mini[3], c; - int n, s; + 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; - result = 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; + } -static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params) -{ - int rc; - char **args; + /* 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 */ - children[0] = fork(); - if (children[0] < 0) { + pid = fork(); + if (pid < 0) { + + /********* can't fork ************/ + + 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]); /* 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); @@ -427,179 +760,128 @@ static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct l _exit(1); } - args = instantiate_arguments(params->master, 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); + 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); - } + /* 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 security mode */ - rc = secmgr_prepare_exec(desc->tag); - if (rc < 0) { - ERROR("call to secmgr_prepare_exec failed: %m"); - _exit(1); - } + /* kill all on error */ + killpg(children[0], SIGKILL); + return -1; +} - /* 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); - } +/* + * 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; - /* fork the slave child */ - children[1] = fork(); - if (children[1] < 0) { - ERROR("slave fork failed: %m"); - _exit(1); + /* 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, 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, 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; } -int afm_launch_initialize() +/* + * 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) { - 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(&launchers); - return rc; + 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, 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); /* init */ children[0] = 0; @@ -607,18 +889,18 @@ int afm_launch(struct afm_launch_desc *desc, pid_t children[2]) /* what launcher ? */ type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE; - ikl = 0; - while (ikl < launchers.count && strcmp(type, launchers.descs[ikl].type)) - ikl++; - if (ikl == launchers.count) { - ERROR("type %s not found!", 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; @@ -626,12 +908,47 @@ int afm_launch(struct afm_launch_desc *desc, pid_t children[2]) /* make the secret and port */ mksecret(secret); + params.uri = uri; params.port = mkport(); params.secret = secret; params.datadir = datadir; - params.master = (const char **)launchers.descs[ikl].execs[0]; - params.slave = (const char **)launchers.descs[ikl].execs[1]; + 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 ? 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; }