afm-launch: allows variable in environment
[src/app-framework-main.git] / src / afm-launch.c
1 /*
2  Copyright 2015, 2016 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 %b bindings                     desc->bindings
491 %c content                      desc->content
492 %D datadir                      params->datadir
493 %H height                       desc->height
494 %h homedir                      desc->home
495 %I icondir                      FWK_ICON_DIR
496 %m mime-type                    desc->type
497 %n name                         desc->name
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 'b':
575                                                 v = "" /*TODO:desc->bindings*/;
576                                                 break;
577                                         case 'c': v = desc->content; break;
578                                         case 'D': v = params->datadir; break;
579                                         case 'H':
580                                                 if(!data)
581                                                         sprintf(height, "%d",
582                                                                 desc->height);
583                                                 v = height;
584                                                 break;
585                                         case 'h': v = desc->home; break;
586                                         case 'I': v = FWK_ICON_DIR; break;
587                                         case 'm': v = desc->type; break;
588                                         case 'n': v = desc->name; break;
589                                         case 'P':
590                                                 if(!data)
591                                                         sprintf(port, "%d",
592                                                                 params->port);
593                                                 v = port;
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, **env;
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         env = instantiate_arguments((const char * const*)environ,
688                                                 desc, params, 1).vector;
689         if (args == NULL || env == NULL) {
690                 close(rpipe[0]);
691                 close(rpipe[1]);
692                 free(args);
693                 free(env);
694                 ERROR("out of memory in master");
695                 errno = ENOMEM;
696                 return -1;
697         }
698
699         /* fork the master child */
700         pid = fork();
701         if (pid < 0) {
702
703                 /********* can't fork ************/
704
705                 close(rpipe[0]);
706                 close(rpipe[1]);
707                 free(args);
708                 free(env);
709                 ERROR("master fork failed: %m");
710                 return -1;
711         }
712         if (pid) {
713
714                 /********* in the parent process ************/
715
716                 close(rpipe[1]);
717                 free(args);
718                 free(env);
719                 pfd.fd = rpipe[0];
720                 pfd.events = POLLIN;
721
722                 /* wait for readyness */
723                 poll(&pfd, 1, ready_timeout);
724                 close(rpipe[0]);
725                 return pid;
726         }
727
728         /********* in the child process ************/
729
730         close(rpipe[0]);
731
732         /* avoid set-gid effect */
733         setresgid(groupid, groupid, groupid);
734
735         /* enter the process group */
736         rc = setpgid(0, progrp);
737         if (rc) {
738                 ERROR("setpgid failed");
739                 _exit(1);
740         }
741
742         /* enter security mode */
743         rc = secmgr_prepare_exec(desc->appid);
744         if (rc < 0) {
745                 ERROR("call to secmgr_prepare_exec failed: %m");
746                 _exit(1);
747         }
748
749         /* enter the datadirectory */
750         rc = mkdir(params->datadir, 0755);
751         if (rc && errno != EEXIST) {
752                 ERROR("creation of datadir %s failed: %m", params->datadir);
753                 _exit(1);
754         }
755         rc = chdir(params->datadir);
756         if (rc) {
757                 ERROR("can't enter the datadir %s: %m", params->datadir);
758                 _exit(1);
759         }
760
761         /* signal if needed */
762         if (!exec->has_readyfd) {
763                 write(rpipe[1], readystr, sizeof(readystr) - 1);
764                 close(rpipe[1]);
765         }
766
767         /* executes the process */
768         rc = execve(args[0], args, env);
769         ERROR("failed to exec master %s: %m", args[0]);
770         _exit(1);
771         return -1;
772 }
773
774 /*
775  * Launches the application 'desc' in local mode using
776  * 'params' and store the resulting pids in 'children'.
777  *
778  * Returns 0 in case of success or -1 in case of error.
779  */
780 static int launch_local(
781         struct afm_launch_desc *desc,
782         pid_t                   children[2],
783         struct launchparam     *params
784 )
785 {
786         /* launches the first, making it group leader */
787         children[0] = launch(desc, params, &params->execs[0], 0);
788         if (children[0] <= 0)
789                 return -1;
790
791         /* nothing more to launch ? */
792         if (params->execs[1].args == NULL)
793                 return 0;
794
795         /* launches the second in the group of the first */
796         children[1] = launch(desc, params, &params->execs[1], children[0]);
797         if (children[1] > 0)
798                 return 0;
799
800         /* kill all on error */
801         killpg(children[0], SIGKILL);
802         return -1;
803 }
804
805 /*
806  * Launches the application 'desc' in remote mode using
807  * 'params' and store the resulting pids in 'children'.
808  *
809  * Returns 0 in case of success or -1 in case of error.
810  */
811 static int launch_remote(
812         struct afm_launch_desc *desc,
813         pid_t                   children[2],
814         struct launchparam     *params
815 )
816 {
817         char *uri;
818
819         /* instanciate the uri */
820         if (params->execs[1].args == NULL)
821                 uri = NULL;
822         else
823                 uri = instantiate_arguments(params->execs[1].args, desc,
824                                                         params, 0).scalar;
825         if (uri == NULL) {
826                 ERROR("out of memory for remote uri");
827                 errno = ENOMEM;
828                 return -1;
829         }
830
831         /* launch the command */
832         children[0] = launch(desc, params, &params->execs[0], 0);
833         if (children[0] <= 0) {
834                 free(uri);
835                 return -1;
836         }
837
838         /* returns the uri in params */
839         *params->uri = uri;
840         return 0;
841 }
842
843 /*
844  * Searchs the launcher descritpion for the given 'type' and 'mode'
845  *
846  * Returns the description found or NULL if nothing matches.
847  */
848 static struct desc_launcher *search_launcher(const char *type,
849                                                 enum afm_launch_mode mode)
850 {
851         struct desc_launcher *dl;
852         struct type_list *tl;
853
854         for (dl = launchers ; dl ; dl = dl->next)
855                 if (dl->mode == mode)
856                         for (tl = dl->types ; tl != NULL ; tl = tl->next)
857                                 if (!strcmp(tl->type, type))
858                                         return dl;
859         return NULL;
860 }
861
862 /*
863  * Launches the application described by 'desc'
864  * and, in case of success, returns the resulting data
865  * in 'children' and 'uri'.
866  *
867  * Returns 0 in case of success or -1 in case of error.
868  */
869 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
870 {
871         int rc;
872         char datadir[PATH_MAX];
873         char secret[9];
874         struct launchparam params;
875         const char *type;
876         struct desc_launcher *dl;
877
878         /* should be init */
879         assert(groupid != 0);
880         assert(is_valid_launch_mode(desc->mode));
881         assert(desc->mode == mode_local || uri != NULL);
882         assert(uri == NULL || *uri == NULL);
883
884         /* init */
885         children[0] = 0;
886         children[1] = 0;
887
888         /* what launcher ? */
889         type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
890         dl = search_launcher(type, desc->mode);
891         if (dl == NULL) {
892                 ERROR("launcher not found for type %s and mode %s!",
893                                 type, name_of_launch_mode(desc->mode));
894                 errno = ENOENT;
895                 return -1;
896         }
897
898         /* prepare paths */
899         rc = snprintf(datadir, sizeof datadir, "%s/%s",
900                                                 desc->home, desc->appid);
901         if (rc < 0 || rc >= (int)sizeof datadir) {
902                 ERROR("overflow for datadir");
903                 errno = EINVAL;
904                 return -1;
905         }
906
907         /* make the secret and port */
908         mksecret(secret);
909         params.uri = uri;
910         params.port = mkport();
911         params.secret = secret;
912         params.datadir = datadir;
913         params.execs = dl->execs;
914
915         switch (desc->mode) {
916         case mode_local:
917                 return launch_local(desc, children, &params);
918         case mode_remote:
919                 return launch_remote(desc, children, &params);
920         default:
921                 assert(0);
922                 return -1;
923         }
924 }
925
926 /*
927  * Initialise the module
928  *
929  * Returns 0 on success or else -1 in case of failure
930  */
931 int afm_launch_initialize()
932 {
933         int rc;
934         gid_t r, e, s;
935
936         /* compute the groupid to set at launch */
937         getresgid(&r, &e, &s);
938         if (s && s != e)
939                 groupid = s; /* the original groupid is used */
940         else
941                 groupid = (gid_t)-1;
942
943         /* reads the configuration file */
944         rc = read_configuration_file(FWK_LAUNCH_CONF);
945         /* dump_launchers(stderr); */
946         
947         return rc;
948 }
949