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