2 Copyright 2015, 2016, 2017 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
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
10 http://www.apache.org/licenses/LICENSE-2.0
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.
30 #include <sys/types.h>
34 extern char **environ;
37 #include "afm-launch-mode.h"
38 #include "afm-launch.h"
39 #include "secmgr-wrap.h"
41 #define DEFAULT_TYPE "text/html"
44 * structure for a launching type
47 struct type_list *next; /* next type */
48 char type[1]; /* type name */
52 * structure for launch vectors
55 int has_readyfd; /* has a request for readyness */
56 const char **args; /* vector of arguments */
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 */
72 struct exec_vector *execs;
76 * Structure for reading the configuration file
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 */
88 * list of launch descriptions
90 struct desc_launcher *launchers = NULL;
93 * the group when launched (to avoid the setgid effect)
95 static gid_t groupid = 0;
98 * separators within configuration files
100 static const char separators[] = " \t\n";
103 * default string emitted when for application not having signal
105 static const char readystr[] = "READY=1";
108 * default time out for readiness of signaling applications
110 static const int ready_timeout = 1500;
112 #if defined(DUMP_LAUNCHERS)
114 * dump all the known launchers to the 'file'
116 static void dump_launchers(FILE *file)
119 struct desc_launcher *desc;
120 struct type_list *type;
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++)
130 desc->execs[j].args[k]);
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
143 static int next_token(struct confread *cread)
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;
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.
157 static int read_line(struct confread *cread)
159 while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
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;
170 if (ferror(cread->file)) {
171 ERROR("%s:%d: error while reading, %m", cread->filepath,
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)
183 static const char **read_vector(struct confread *cread)
188 unsigned count, length;
191 index0 = cread->index;
192 length0 = cread->length;
197 while(cread->length) {
199 length += (unsigned)cread->length;
204 cread->index = index0;
205 cread->length = length0;
206 vector = malloc(length + count + (count + 1) * sizeof(char*));
211 args = (char*)(vector + count + 1);
213 while(cread->length) {
214 vector[count++] = args;
215 memcpy(args, &cread->buffer[cread->index],
216 (unsigned)cread->length);
217 args += cread->length;
221 vector[count] = NULL;
222 cread->index = index0;
223 cread->length = length0;
228 * Reads the type from 'cread' directly in the list item and return it.
229 * returns NULL in case or error.
231 * - EINVAL extra characters
232 * - ENOMEM memory depletion
234 static struct type_list *read_type(struct confread *cread)
237 struct type_list *result;
239 /* record index and length */
240 index = cread->index;
241 length = cread->length;
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]);
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);
260 /* fill the structure */
261 memcpy(result->type, &cread->buffer[index], (unsigned)length);
262 result->type[length] = 0;
267 * Reads the mode from 'cread' and return it.
268 * returns invalid_launch_mode in case or error.
270 * - EINVAL no mode or extra characters or invalid mode
272 static enum afm_launch_mode read_mode(struct confread *cread)
275 enum afm_launch_mode result;
277 assert(cread->index == 0);
278 assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
280 /* get the next token: the mode string */
281 if (!next_token(cread)) {
282 ERROR("%s:%d: no mode value set", cread->filepath,
285 return invalid_launch_mode;
288 /* record index and length */
289 index = cread->index;
290 length = cread->length;
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]);
298 return invalid_launch_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]);
313 * free the memory used by 'types'
315 static void free_type_list(struct type_list *types)
317 while (types != NULL) {
318 struct type_list *next = types->next;
325 * reads the configuration file handled by 'cread'
326 * and adds its contents to the launcher list
328 * returns 0 in case of success or -1 in case of error.
330 static int read_launchers(struct confread *cread)
333 struct type_list *types, *lt;
334 struct desc_launcher *desc;
335 enum afm_launch_mode mode;
342 mode = invalid_launch_mode;
343 rc = read_line(cread);
345 if (cread->index == 0) {
346 if (cread->length == 4
347 && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
348 /* check if allowed */
350 ERROR("%s:%d: mode found before"
355 free_type_list(types);
360 mode = read_mode(cread);
361 if (mode == invalid_launch_mode)
364 if (mode == invalid_launch_mode) {
365 ERROR("%s:%d: mode not found"
370 assert(types == NULL);
374 lt = read_type(cread);
376 free_type_list(types);
383 } else if (types == NULL && desc == NULL) {
385 ERROR("%s:%d: untyped launch vector found",
386 cread->filepath, cread->lineno);
388 ERROR("%s:%d: extra launch vector found"
389 " (the maximum count is 2)",
390 cread->filepath, cread->lineno);
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);
405 assert(desc == NULL);
406 desc = malloc(sizeof * desc);
408 ERROR("%s:%d: out of memory",
409 cread->filepath, cread->lineno);
410 free_type_list(types);
414 desc->next = launchers;
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;
424 desc->execs[1].has_readyfd = has_readyfd;
425 desc->execs[1].args = vector;
429 rc = read_line(cread);
432 ERROR("%s:%d: end of file found before launch vector",
433 cread->filepath, cread->lineno);
434 free_type_list(types);
442 * reads the configuration file 'filepath'
443 * and adds its contents to the launcher list
445 * returns 0 in case of success or -1 in case of error.
447 static int read_configuration_file(const char *filepath)
450 struct confread cread;
452 /* opens the configuration file */
453 cread.file = fopen(filepath, "r");
454 if (cread.file == NULL) {
456 ERROR("can't read file %s: %m", filepath);
460 cread.filepath = filepath;
462 rc = read_launchers(&cread);
469 * Creates a secret in 'buffer'
471 static void mksecret(char buffer[9])
473 snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
477 * Allocates a port and return it.
481 static int port_ring = 12345;
482 int port = port_ring;
483 if (port < 12345 || port > 15432)
485 port_ring = port + 1;
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
501 %r rootdir desc->path
502 %R readyfd params->readyfd
503 %S secret params->secret
508 * Union for handling either scalar arguments or vectorial arguments.
511 char *scalar; /* string of space separated arguments */
512 char **vector; /* vector of arguments */
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'.
522 * The vectors are made of an array pointers terminated by
525 * Returns the resulting value or NULL in case of error
527 static union arguments instantiate_arguments(
528 const char * const *args,
529 struct afm_launch_desc *desc,
530 struct launchparam *params,
534 const char * const *iter;
538 union arguments result;
539 char port[20], width[20], height[20], readyfd[20], mini[3];
542 sep = wants_vector ? 0 : ' ';
547 * loop that either compute the size and build the result
548 * advantage: appears only in one place
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 */
555 /* iterate over arguments */
556 for (n = 0, iter = args ; (p = *iter) != NULL ; iter++) {
557 /* init the vector */
559 result.vector[n] = data;
562 /* scan the argument */
563 while((c = *p++) != 0) {
565 /* standard character */
572 /* (convert num->string only once) */
575 case 'a': v = desc->appid; break;
577 v = "" /*TODO:desc->bindings*/;
579 case 'c': v = desc->content; break;
580 case 'D': v = params->datadir; break;
583 sprintf(height, "%d",
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;
599 sprintf(readyfd, "%d",
603 case 'r': v = desc->path; break;
604 case 'S': v = params->secret; break;
620 data = stpcpy(data, v);
622 s += (unsigned)strlen(v);
625 /* terminate the argument */
632 /* first iteration: allocation */
634 result.scalar = malloc(s);
635 data = result.scalar;
637 result.vector = malloc((n+1)*sizeof(char*) + s);
638 if (result.vector != NULL)
639 data = (char*)(&result.vector[n + 1]);
646 /* second iteration: termination */
650 result.vector[n] = NULL;
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'.
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.
665 * Returns the pid of the created process or -1 in case of error.
668 struct afm_launch_desc *desc,
669 struct launchparam *params,
670 struct exec_vector *exec,
680 /* prepare the pipes */
683 ERROR("error while calling pipe2: %m");
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) {
697 ERROR("out of memory in master");
702 /* fork the master child */
706 /********* can't fork ************/
712 ERROR("master fork failed: %m");
717 /********* in the parent process ************/
725 /* wait for readyness */
726 poll(&pfd, 1, ready_timeout);
731 /********* in the child process ************/
735 /* set name by appid */
736 verbose_set_name(desc->appid, 0);
738 /* avoid set-gid effect */
739 setresgid(groupid, groupid, groupid);
741 /* enter the process group */
742 rc = setpgid(0, progrp);
744 ERROR("setpgid failed");
748 /* enter security mode */
749 rc = secmgr_prepare_exec(desc->appid);
751 ERROR("call to secmgr_prepare_exec failed: %m");
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);
761 rc = chdir(params->datadir);
763 ERROR("can't enter the datadir %s: %m", params->datadir);
767 /* signal if needed */
768 if (!exec->has_readyfd) {
769 write(rpipe[1], readystr, sizeof(readystr) - 1);
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]);
782 * Launches the application 'desc' in local mode using
783 * 'params' and store the resulting pids in 'children'.
785 * Returns 0 in case of success or -1 in case of error.
787 static int launch_local(
788 struct afm_launch_desc *desc,
790 struct launchparam *params
793 /* launches the first, making it group leader */
794 children[0] = launch(desc, params, ¶ms->execs[0], 0);
795 if (children[0] <= 0)
798 /* nothing more to launch ? */
799 if (params->execs[1].args == NULL)
802 /* launches the second in the group of the first */
803 children[1] = launch(desc, params, ¶ms->execs[1], children[0]);
807 /* kill all on error */
808 killpg(children[0], SIGKILL);
813 * Launches the application 'desc' in remote mode using
814 * 'params' and store the resulting pids in 'children'.
816 * Returns 0 in case of success or -1 in case of error.
818 static int launch_remote(
819 struct afm_launch_desc *desc,
821 struct launchparam *params
826 /* instanciate the uri */
827 if (params->execs[1].args == NULL)
830 uri = instantiate_arguments(params->execs[1].args, desc,
833 ERROR("out of memory for remote uri");
838 /* launch the command */
839 children[0] = launch(desc, params, ¶ms->execs[0], 0);
840 if (children[0] <= 0) {
845 /* returns the uri in params */
851 * Searchs the launcher descritpion for the given 'type' and 'mode'
853 * Returns the description found or NULL if nothing matches.
855 static struct desc_launcher *search_launcher(const char *type,
856 enum afm_launch_mode mode)
858 struct desc_launcher *dl;
859 struct type_list *tl;
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))
870 * Launches the application described by 'desc'
871 * and, in case of success, returns the resulting data
872 * in 'children' and 'uri'.
874 * Returns 0 in case of success or -1 in case of error.
876 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
879 char datadir[PATH_MAX];
881 struct launchparam params;
883 struct desc_launcher *dl;
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);
895 /* what launcher ? */
896 type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
897 dl = search_launcher(type, desc->mode);
899 ERROR("launcher not found for type %s and mode %s!",
900 type, name_of_launch_mode(desc->mode));
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");
914 /* make the secret and port */
917 params.port = mkport();
918 params.secret = secret;
919 params.datadir = datadir;
920 params.execs = dl->execs;
922 switch (desc->mode) {
924 return launch_local(desc, children, ¶ms);
926 return launch_remote(desc, children, ¶ms);
934 * Initialise the module
936 * Returns 0 on success or else -1 in case of failure
938 int afm_launch_initialize()
943 /* compute the groupid to set at launch */
944 getresgid(&r, &e, &s);
946 groupid = s; /* the original groupid is used */
950 /* reads the configuration file */
951 rc = read_configuration_file(FWK_LAUNCH_CONF);
952 #if defined(DUMP_LAUNCHERS)
954 dump_launchers(stderr);