doc: add new document quick-tutorial
[src/app-framework-main.git] / src / afm-launch.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18
19 #define _GNU_SOURCE
20
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <limits.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <assert.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <poll.h>
32 #include <signal.h>
33
34 extern char **environ;
35
36 #include "verbose.h"
37 #include "afm-launch-mode.h"
38 #include "afm-launch.h"
39 #include "secmgr-wrap.h"
40
41 #define DEFAULT_TYPE "text/html"
42
43 /*
44  * structure for a launching type
45  */
46 struct type_list {
47         struct type_list *next; /* next type */
48         char type[1];           /* type name */
49 };
50
51 /*
52  * structure for launch vectors
53  */
54 struct exec_vector {
55         int has_readyfd;     /* has a request for readyness */
56         const char **args;   /* vector of arguments */
57 };
58
59 struct desc_launcher {
60         struct desc_launcher *next;   /* next launcher description */
61         enum afm_launch_mode mode;    /* the launch mode */
62         struct type_list *types;      /* the launched types */
63         struct exec_vector execs[2];  /* the launching vectors */
64 };
65
66 struct launchparam {
67         int port;
68         int readyfd;
69         char **uri;
70         const char *secret;
71         const char *datadir;
72         struct exec_vector *execs;
73 };
74
75 /*
76  * Structure for reading the configuration file
77  */
78 struct confread {
79         const char *filepath; /* path of the configuration file */
80         FILE *file;           /* handle to the file */
81         int lineno;           /* current line number */
82         int index;            /* start of the current token (in buffer) */
83         int length;           /* length of the current token */
84         char buffer[4096];    /* current line */
85 };
86
87 /*
88  * list of launch descriptions
89  */
90 struct desc_launcher *launchers = NULL;
91
92 /*
93  * the group when launched (to avoid the setgid effect)
94  */
95 static gid_t groupid = 0;
96
97 /*
98  * separators within configuration files
99  */
100 static const char separators[] = " \t\n";
101
102 /*
103  * default string emitted when for application not having signal
104  */
105 static const char readystr[] = "READY=1";
106
107 /*
108  * default time out for readiness of signaling applications
109  */
110 static const int ready_timeout = 1500;
111
112 /*
113  * dump all the known launchers to the 'file'
114  */
115 static void dump_launchers(FILE *file)
116 {
117         int j, k;
118         struct desc_launcher *desc;
119         struct type_list *type;
120
121         for (desc = launchers ; desc != NULL ; desc = desc->next) {
122                 fprintf(file, "mode %s\n", name_of_launch_mode(desc->mode));
123                 for (type = desc->types ; type != NULL ; type = type->next)
124                         fprintf(file, "%s\n", type->type);
125                 for ( j = 0 ; j < 2 ; j++)
126                         if (desc->execs[j].args != NULL) {
127                                 for (k = 0; desc->execs[j].args[k] != NULL; k++)
128                                         fprintf(file, "  %s",
129                                                         desc->execs[j].args[k]);
130                                 fprintf(file, "\n");
131                         }
132                 fprintf(file, "\n");
133         }
134 }
135
136 /*
137  * update 'cread' to point the the next token
138  * returns the length of the token that is nul if no
139  * more token exists in the line
140  */
141 static int next_token(struct confread *cread)
142 {
143         int idx = cread->index + cread->length;
144         cread->index = idx + (int)strspn(&cread->buffer[idx], separators);
145         cread->length = (int)strcspn(&cread->buffer[cread->index], separators);
146         return cread->length;
147 }
148
149 /*
150  * reads the next line, skipping empty lines or lines
151  * having only comments.
152  * returns either 0 at end of the file, -1 in case of error or
153  * in case of success the length of the first token.
154  */
155 static int read_line(struct confread *cread)
156 {
157         while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
158                 cread->lineno++;
159                 cread->index = (int)strspn(cread->buffer, separators);
160                 if (cread->buffer[cread->index]
161                   && cread->buffer[cread->index] != '#') {
162                         cread->length = (int)strcspn(
163                                 &cread->buffer[cread->index], separators);
164                         assert(cread->length > 0);
165                         return cread->length;
166                 }
167         }
168         if (ferror(cread->file)) {
169                 ERROR("%s:%d: error while reading, %m", cread->filepath,
170                                                                 cread->lineno);
171                 return -1;
172         }
173         return 0;
174 }
175
176 /*
177  * extract from 'cread' a launch vector that is allocated in
178  * one piece of memory.
179  * 'cread' is left unchanged (index and length are not changed)
180  */
181 static const char **read_vector(struct confread *cread)
182 {
183         int index0, length0;
184         const char **vector;
185         char *args;
186         unsigned count, length;
187
188         /* record origin */
189         index0 = cread->index;
190         length0 = cread->length;
191
192         /* count */
193         count = 0;
194         length = 0;
195         while(cread->length) {
196                 count++;
197                 length += (unsigned)cread->length;
198                 next_token(cread);
199         }
200
201         /* allocates */
202         cread->index = index0;
203         cread->length = length0;
204         vector = malloc(length + count + (count + 1) * sizeof(char*));
205         if (vector == NULL)
206                 return NULL;
207
208         /* copies */
209         args = (char*)(vector + count + 1);
210         count = 0;
211         while(cread->length) {
212                 vector[count++] = args;
213                 memcpy(args, &cread->buffer[cread->index],
214                                                 (unsigned)cread->length);
215                 args += cread->length;
216                 *args++ = 0;
217                 next_token(cread);
218         }
219         vector[count] = NULL;
220         cread->index = index0;
221         cread->length = length0;
222         return vector;
223 }
224
225 /*
226  * Reads the type from 'cread' directly in the list item and return it.
227  * returns NULL in case or error.
228  * errno:
229  *  - EINVAL     extra characters
230  *  - ENOMEM     memory depletion
231  */
232 static struct type_list *read_type(struct confread *cread)
233 {
234         int index, length;
235         struct type_list *result;
236
237         /* record index and length */
238         index = cread->index;
239         length = cread->length;
240
241         /* check no extra characters */
242         if (next_token(cread)) {
243                 ERROR("%s:%d: extra characters found after type %.*s",
244                         cread->filepath, cread->lineno, length,
245                                                 &cread->buffer[index]);
246                 errno = EINVAL;
247                 return NULL;
248         }
249
250         /* allocate structure */
251         result = malloc(sizeof(struct type_list) + (unsigned)length);
252         if (result == NULL) {
253                 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
254                 errno = ENOMEM;
255                 return NULL;
256         }
257
258         /* fill the structure */
259         memcpy(result->type, &cread->buffer[index], (unsigned)length);
260         result->type[length] = 0;
261         return result;
262 }
263
264 /*
265  * Reads the mode from 'cread' and return it.
266  * returns invalid_launch_mode in case or error.
267  * errno:
268  *  - EINVAL     no mode or extra characters or invalid mode
269  */
270 static enum afm_launch_mode read_mode(struct confread *cread)
271 {
272         int index, length;
273         enum afm_launch_mode result;
274
275         assert(cread->index == 0);
276         assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
277
278         /* get the next token: the mode string */
279         if (!next_token(cread)) {
280                 ERROR("%s:%d: no mode value set", cread->filepath,
281                                                         cread->lineno);
282                 errno = EINVAL;
283                 return invalid_launch_mode;
284         }
285
286         /* record index and length */
287         index = cread->index;
288         length = cread->length;
289
290         /* check no extra characters */
291         if (next_token(cread)) {
292                 ERROR("%s:%d: extra characters found after mode %.*s",
293                         cread->filepath, cread->lineno, length,
294                                                 &cread->buffer[index]);
295                 errno = EINVAL;
296                 return invalid_launch_mode;
297         }
298
299         /* get the mode */
300         cread->buffer[index + length] = 0;
301         result = launch_mode_of_name(&cread->buffer[index]);
302         if (result == invalid_launch_mode) {
303                 ERROR("%s:%d: invalid mode value %s",
304                         cread->filepath, cread->lineno, &cread->buffer[index]);
305                 errno = EINVAL;
306         }
307         return result;
308 }
309
310 /*
311  * free the memory used by 'types'
312  */
313 static void free_type_list(struct type_list *types)
314 {
315         while (types != NULL) {
316                 struct type_list *next = types->next;
317                 free(types);
318                 types = next;
319         }
320 }
321
322 /*
323  * reads the configuration file handled by 'cread'
324  * and adds its contents to the launcher list
325  *
326  * returns 0 in case of success or -1 in case of error.
327  */
328 static int read_launchers(struct confread *cread)
329 {
330         int rc, has_readyfd;
331         struct type_list *types, *lt;
332         struct desc_launcher *desc;
333         enum afm_launch_mode mode;
334         const char **vector;
335
336         /* reads the file */
337         lt = NULL;
338         types = NULL;
339         desc = NULL;
340         mode = invalid_launch_mode;
341         rc = read_line(cread);
342         while (rc > 0) {
343                 if (cread->index == 0) {
344                         if (cread->length == 4
345                         && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
346                                 /* check if allowed */
347                                 if (types != NULL) {
348                                         ERROR("%s:%d: mode found before"
349                                                         " launch vector",
350                                                         cread->filepath,
351                                                         cread->lineno);
352                                         errno = EINVAL;
353                                         free_type_list(types);
354                                         return -1;
355                                 }
356
357                                 /* read the mode */
358                                 mode = read_mode(cread);
359                                 if (mode == invalid_launch_mode)
360                                         return -1;
361                         } else {
362                                 if (mode == invalid_launch_mode) {
363                                         ERROR("%s:%d: mode not found"
364                                                         " before type",
365                                                         cread->filepath,
366                                                         cread->lineno);
367                                         errno = EINVAL;
368                                         assert(types == NULL);
369                                         return -1;
370                                 }
371                                 /* read a type */
372                                 lt = read_type(cread);
373                                 if (lt == NULL) {
374                                         free_type_list(types);
375                                         return -1;
376                                 }
377                                 lt->next = types;
378                                 types = lt;
379                         }
380                         desc = NULL;
381                 } else if (types == NULL && desc == NULL) {
382                         if (lt == NULL)
383                                 ERROR("%s:%d: untyped launch vector found",
384                                         cread->filepath, cread->lineno);
385                         else
386                                 ERROR("%s:%d: extra launch vector found"
387                                         " (the maximum count is 2)",
388                                         cread->filepath, cread->lineno);
389                         errno = EINVAL;
390                         return -1;
391                 } else {
392                         has_readyfd = NULL != strstr(
393                                         &cread->buffer[cread->index], "%R");
394                         vector = read_vector(cread);
395                         if (vector == NULL) {
396                                 ERROR("%s:%d: out of memory",
397                                         cread->filepath, cread->lineno);
398                                 free_type_list(types);
399                                 errno = ENOMEM;
400                                 return -1;
401                         }
402                         if (types) {
403                                 assert(desc == NULL);
404                                 desc = malloc(sizeof * desc);
405                                 if (desc == NULL) {
406                                         ERROR("%s:%d: out of memory",
407                                                 cread->filepath, cread->lineno);
408                                         free_type_list(types);
409                                         errno = ENOMEM;
410                                         return -1;
411                                 }
412                                 desc->next = launchers;
413                                 desc->mode = mode;
414                                 desc->types = types;
415                                 desc->execs[0].has_readyfd = has_readyfd;
416                                 desc->execs[0].args = vector;
417                                 desc->execs[1].has_readyfd = 0;
418                                 desc->execs[1].args = NULL;
419                                 types = NULL;
420                                 launchers = desc;
421                         } else {
422                                 desc->execs[1].has_readyfd = has_readyfd;
423                                 desc->execs[1].args = vector;
424                                 desc = NULL;
425                         }
426                 }
427                 rc = read_line(cread);
428         }
429         if (types != NULL) {
430                 ERROR("%s:%d: end of file found before launch vector",
431                         cread->filepath, cread->lineno);
432                 free_type_list(types);
433                 errno = EINVAL;
434                 return -1;
435         }
436         return rc;
437 }
438
439 /*
440  * reads the configuration file 'filepath'
441  * and adds its contents to the launcher list
442  *
443  * returns 0 in case of success or -1 in case of error.
444  */
445 static int read_configuration_file(const char *filepath)
446 {
447         int rc;
448         struct confread cread;
449
450         /* opens the configuration file */
451         cread.file = fopen(filepath, "r");
452         if (cread.file == NULL) {
453                 /* error */
454                 ERROR("can't read file %s: %m", filepath);
455                 rc = -1;
456         } else {
457                 /* reads it */
458                 cread.filepath = filepath;
459                 cread.lineno = 0;
460                 rc = read_launchers(&cread);
461                 fclose(cread.file);
462         }
463         return rc;
464 }
465
466 /*
467  * Creates a secret in 'buffer'
468  */
469 static void mksecret(char buffer[9])
470 {
471         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
472 }
473
474 /*
475  * Allocates a port and return it.
476  */
477 static int mkport()
478 {
479         static int port_ring = 12345;
480         int port = port_ring;
481         if (port < 12345 || port > 15432)
482                 port = 12345;
483         port_ring = port + 1;
484         return port;
485 }
486
487 /*
488 %% %
489 %a appid                        desc->appid
490 %c content                      desc->content
491 %D datadir                      params->datadir
492 %H height                       desc->height
493 %h homedir                      desc->home
494 %I icondir                      FWK_ICON_DIR
495 %m mime-type                    desc->type
496 %n name                         desc->name
497 %p plugins                      desc->plugins
498 %P port                         params->port
499 %r rootdir                      desc->path
500 %R readyfd                      params->readyfd
501 %S secret                       params->secret
502 %W width                        desc->width
503 */
504
505 /*
506  * Union for handling either scalar arguments or vectorial arguments.
507  */
508 union arguments {
509         char *scalar;  /* string of space separated arguments */
510         char **vector; /* vector of arguments */
511 };
512
513 /*
514  * Computes the substitutions of 'args' according to the
515  * data of 'desc' and 'params'. The result is a single
516  * piece of memory (that must be freed in one time)
517  * containing either a vector or a string depending on
518  * the value of 'wants_vector'.
519  *
520  * The vectors are made of an array pointers terminated by
521  * the NULL pointer.
522  *
523  * Returns the resulting value or NULL in case of error
524  */
525 static union arguments instantiate_arguments(
526         const char * const     *args,
527         struct afm_launch_desc *desc,
528         struct launchparam     *params,
529         int                     wants_vector
530 )
531 {
532         const char * const *iter;
533         const char *p, *v;
534         char *data, c, sep;
535         unsigned n, s;
536         union arguments result;
537         char port[20], width[20], height[20], readyfd[20], mini[3];
538
539         /* init */
540         sep = wants_vector ? 0 : ' ';
541         mini[0] = '%';
542         mini[2] = 0;
543
544         /*
545          * loop that either compute the size and build the result
546          * advantage: appears only in one place
547          */
548         result.vector = NULL; /* initialise both to avoid */
549         result.scalar = NULL; /* a very stupid compiler warning */
550         data = NULL;          /* no data for the first iteration */
551         n = s = 0;
552         for (;;) {
553                 /* iterate over arguments */
554                 for (n = 0, iter = args ; (p = *iter) != NULL ; iter++) {
555                         /* init the vector */
556                         if (data && !sep)
557                                 result.vector[n] = data;
558                         n++;
559
560                         /* scan the argument */
561                         while((c = *p++) != 0) {
562                                 if (c != '%') {
563                                         /* standard character */
564                                         if (data)
565                                                 *data++ = c;
566                                         else
567                                                 s++;
568                                 } else {
569                                         /* substitutions */
570                                         /* (convert num->string only once) */
571                                         c = *p++;
572                                         switch (c) {
573                                         case 'a': v = desc->appid; break;
574                                         case 'c': v = desc->content; break;
575                                         case 'D': v = params->datadir; break;
576                                         case 'H':
577                                                 if(!data)
578                                                         sprintf(height, "%d",
579                                                                 desc->height);
580                                                 v = height;
581                                                 break;
582                                         case 'h': v = desc->home; break;
583                                         case 'I': v = FWK_ICON_DIR; break;
584                                         case 'm': v = desc->type; break;
585                                         case 'n': v = desc->name; break;
586                                         case 'P':
587                                                 if(!data)
588                                                         sprintf(port, "%d",
589                                                                 params->port);
590                                                 v = port;
591                                                 break;
592                                         case 'p':
593                                                 v = "" /*TODO:desc->plugins*/;
594                                                 break;
595                                         case 'R':
596                                                 if(!data)
597                                                         sprintf(readyfd, "%d",
598                                                              params->readyfd);
599                                                 v = readyfd;
600                                                 break;
601                                         case 'r': v = desc->path; break;
602                                         case 'S': v = params->secret; break;
603                                         case 'W':
604                                                 if(!data)
605                                                         sprintf(width, "%d",
606                                                                 desc->width);
607                                                 v = width;
608                                                 break;
609                                         case '%':
610                                                 c = 0;
611                                         default:
612                                                 mini[1] = c;
613                                                 v = mini;
614                                                 break;
615                                         }
616                                         if (data)
617                                                 data = stpcpy(data, v);
618                                         else
619                                                 s += (unsigned)strlen(v);
620                                 }
621                         }
622                         /* terminate the argument */
623                         if (data)
624                                 *data++ = sep;
625                         else
626                                 s++;
627                 }
628                 if (!data) {
629                         /* first iteration: allocation */
630                         if (sep) {
631                                 result.scalar = malloc(s);
632                                 data = result.scalar;
633                         } else {
634                                 result.vector = malloc((n+1)*sizeof(char*) + s);
635                                 if (result.vector != NULL)
636                                         data = (char*)(&result.vector[n + 1]);
637                         }
638                         if (!data) {
639                                 errno = ENOMEM;
640                                 return result;
641                         }
642                 } else {
643                         /* second iteration: termination */
644                         if (sep)
645                                 *--data = 0;
646                         else
647                                 result.vector[n] = NULL;
648                         return result;
649                 }
650         }
651 }
652
653 /*
654  * Launchs (fork-execs) the program described by 'exec'
655  * using the parameters of 'desc' and 'params' to instantiate
656  * it. The created process is attached to the process group 'progrp'.
657  *
658  * After being created and before to be launched, the process
659  * is put in its security environment and its directory is
660  * changed to params->datadir.
661  *
662  * Returns the pid of the created process or -1 in case of error.
663  */
664 static pid_t launch(
665         struct afm_launch_desc *desc,
666         struct launchparam     *params,
667         struct exec_vector     *exec,
668         pid_t                   progrp
669 )
670 {
671         int rc;
672         char **args;
673         pid_t pid;
674         int rpipe[2];
675         struct pollfd pfd;
676
677         /* prepare the pipes */
678         rc = pipe(rpipe);
679         if (rc < 0) {
680                 ERROR("error while calling pipe2: %m");
681                 return -1;
682         }
683
684         /* instanciate the arguments */
685         params->readyfd = rpipe[1];
686         args = instantiate_arguments(exec->args, desc, params, 1).vector;
687         if (args == NULL) {
688                 close(rpipe[0]);
689                 close(rpipe[1]);
690                 ERROR("out of memory in master");
691                 errno = ENOMEM;
692                 return -1;
693         }
694
695         /* fork the master child */
696         pid = fork();
697         if (pid < 0) {
698
699                 /********* can't fork ************/
700
701                 close(rpipe[0]);
702                 close(rpipe[1]);
703                 free(args);
704                 ERROR("master fork failed: %m");
705                 return -1;
706         }
707         if (pid) {
708
709                 /********* in the parent process ************/
710
711                 close(rpipe[1]);
712                 free(args);
713                 pfd.fd = rpipe[0];
714                 pfd.events = POLLIN;
715
716                 /* wait for readyness */
717                 poll(&pfd, 1, ready_timeout);
718                 close(rpipe[0]);
719                 return pid;
720         }
721
722         /********* in the child process ************/
723
724         close(rpipe[0]);
725
726         /* avoid set-gid effect */
727         setresgid(groupid, groupid, groupid);
728
729         /* enter the process group */
730         rc = setpgid(0, progrp);
731         if (rc) {
732                 ERROR("setpgid failed");
733                 _exit(1);
734         }
735
736         /* enter security mode */
737         rc = secmgr_prepare_exec(desc->appid);
738         if (rc < 0) {
739                 ERROR("call to secmgr_prepare_exec failed: %m");
740                 _exit(1);
741         }
742
743         /* enter the datadirectory */
744         rc = mkdir(params->datadir, 0755);
745         if (rc && errno != EEXIST) {
746                 ERROR("creation of datadir %s failed: %m", params->datadir);
747                 _exit(1);
748         }
749         rc = chdir(params->datadir);
750         if (rc) {
751                 ERROR("can't enter the datadir %s: %m", params->datadir);
752                 _exit(1);
753         }
754
755         /* signal if needed */
756         if (!exec->has_readyfd) {
757                 write(rpipe[1], readystr, sizeof(readystr) - 1);
758                 close(rpipe[1]);
759         }
760
761         /* executes the process */
762         rc = execve(args[0], args, environ);
763         ERROR("failed to exec master %s: %m", args[0]);
764         _exit(1);
765         return -1;
766 }
767
768 /*
769  * Launches the application 'desc' in local mode using
770  * 'params' and store the resulting pids in 'children'.
771  *
772  * Returns 0 in case of success or -1 in case of error.
773  */
774 static int launch_local(
775         struct afm_launch_desc *desc,
776         pid_t                   children[2],
777         struct launchparam     *params
778 )
779 {
780         /* launches the first, making it group leader */
781         children[0] = launch(desc, params, &params->execs[0], 0);
782         if (children[0] <= 0)
783                 return -1;
784
785         /* nothing more to launch ? */
786         if (params->execs[1].args == NULL)
787                 return 0;
788
789         /* launches the second in the group of the first */
790         children[1] = launch(desc, params, &params->execs[1], children[0]);
791         if (children[1] > 0)
792                 return 0;
793
794         /* kill all on error */
795         killpg(children[0], SIGKILL);
796         return -1;
797 }
798
799 /*
800  * Launches the application 'desc' in remote mode using
801  * 'params' and store the resulting pids in 'children'.
802  *
803  * Returns 0 in case of success or -1 in case of error.
804  */
805 static int launch_remote(
806         struct afm_launch_desc *desc,
807         pid_t                   children[2],
808         struct launchparam     *params
809 )
810 {
811         char *uri;
812
813         /* instanciate the uri */
814         if (params->execs[1].args == NULL)
815                 uri = NULL;
816         else
817                 uri = instantiate_arguments(params->execs[1].args, desc,
818                                                         params, 0).scalar;
819         if (uri == NULL) {
820                 ERROR("out of memory for remote uri");
821                 errno = ENOMEM;
822                 return -1;
823         }
824
825         /* launch the command */
826         children[0] = launch(desc, params, &params->execs[0], 0);
827         if (children[0] <= 0) {
828                 free(uri);
829                 return -1;
830         }
831
832         /* returns the uri in params */
833         *params->uri = uri;
834         return 0;
835 }
836
837 /*
838  * Searchs the launcher descritpion for the given 'type' and 'mode'
839  *
840  * Returns the description found or NULL if nothing matches.
841  */
842 static struct desc_launcher *search_launcher(const char *type,
843                                                 enum afm_launch_mode mode)
844 {
845         struct desc_launcher *dl;
846         struct type_list *tl;
847
848         for (dl = launchers ; dl ; dl = dl->next)
849                 if (dl->mode == mode)
850                         for (tl = dl->types ; tl != NULL ; tl = tl->next)
851                                 if (!strcmp(tl->type, type))
852                                         return dl;
853         return NULL;
854 }
855
856 /*
857  * Launches the application described by 'desc'
858  * and, in case of success, returns the resulting data
859  * in 'children' and 'uri'.
860  *
861  * Returns 0 in case of success or -1 in case of error.
862  */
863 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
864 {
865         int rc;
866         char datadir[PATH_MAX];
867         char secret[9];
868         struct launchparam params;
869         const char *type;
870         struct desc_launcher *dl;
871
872         /* should be init */
873         assert(groupid != 0);
874         assert(is_valid_launch_mode(desc->mode));
875         assert(desc->mode == mode_local || uri != NULL);
876         assert(uri == NULL || *uri == NULL);
877
878         /* init */
879         children[0] = 0;
880         children[1] = 0;
881
882         /* what launcher ? */
883         type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
884         dl = search_launcher(type, desc->mode);
885         if (dl == NULL) {
886                 ERROR("launcher not found for type %s and mode %s!",
887                                 type, name_of_launch_mode(desc->mode));
888                 errno = ENOENT;
889                 return -1;
890         }
891
892         /* prepare paths */
893         rc = snprintf(datadir, sizeof datadir, "%s/%s",
894                                                 desc->home, desc->appid);
895         if (rc < 0 || rc >= (int)sizeof datadir) {
896                 ERROR("overflow for datadir");
897                 errno = EINVAL;
898                 return -1;
899         }
900
901         /* make the secret and port */
902         mksecret(secret);
903         params.uri = uri;
904         params.port = mkport();
905         params.secret = secret;
906         params.datadir = datadir;
907         params.execs = dl->execs;
908
909         switch (desc->mode) {
910         case mode_local:
911                 return launch_local(desc, children, &params);
912         case mode_remote:
913                 return launch_remote(desc, children, &params);
914         default:
915                 assert(0);
916                 return -1;
917         }
918 }
919
920 /*
921  * Initialise the module
922  *
923  * Returns 0 on success or else -1 in case of failure
924  */
925 int afm_launch_initialize()
926 {
927         int rc;
928         gid_t r, e, s;
929
930         /* compute the groupid to set at launch */
931         getresgid(&r, &e, &s);
932         if (s && s != e)
933                 groupid = s; /* the original groupid is used */
934         else
935                 groupid = (gid_t)-1;
936
937         rc = read_configuration_file(FWK_LAUNCH_CONF);
938         /* dump_launchers(stderr); */
939         return rc;
940 }
941