+/*
+ * 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 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 */