2 Copyright 2015, 2016 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;
113 * dump all the known launchers to the 'file'
115 static void dump_launchers(FILE *file)
118 struct desc_launcher *desc;
119 struct type_list *type;
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++)
129 desc->execs[j].args[k]);
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
141 static int next_token(struct confread *cread)
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;
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.
155 static int read_line(struct confread *cread)
157 while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
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;
168 if (ferror(cread->file)) {
169 ERROR("%s:%d: error while reading, %m", cread->filepath,
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)
181 static const char **read_vector(struct confread *cread)
186 unsigned count, length;
189 index0 = cread->index;
190 length0 = cread->length;
195 while(cread->length) {
197 length += (unsigned)cread->length;
202 cread->index = index0;
203 cread->length = length0;
204 vector = malloc(length + count + (count + 1) * sizeof(char*));
209 args = (char*)(vector + count + 1);
211 while(cread->length) {
212 vector[count++] = args;
213 memcpy(args, &cread->buffer[cread->index],
214 (unsigned)cread->length);
215 args += cread->length;
219 vector[count] = NULL;
220 cread->index = index0;
221 cread->length = length0;
226 * Reads the type from 'cread' directly in the list item and return it.
227 * returns NULL in case or error.
229 * - EINVAL extra characters
230 * - ENOMEM memory depletion
232 static struct type_list *read_type(struct confread *cread)
235 struct type_list *result;
237 /* record index and length */
238 index = cread->index;
239 length = cread->length;
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]);
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);
258 /* fill the structure */
259 memcpy(result->type, &cread->buffer[index], (unsigned)length);
260 result->type[length] = 0;
265 * Reads the mode from 'cread' and return it.
266 * returns invalid_launch_mode in case or error.
268 * - EINVAL no mode or extra characters or invalid mode
270 static enum afm_launch_mode read_mode(struct confread *cread)
273 enum afm_launch_mode result;
275 assert(cread->index == 0);
276 assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
278 /* get the next token: the mode string */
279 if (!next_token(cread)) {
280 ERROR("%s:%d: no mode value set", cread->filepath,
283 return invalid_launch_mode;
286 /* record index and length */
287 index = cread->index;
288 length = cread->length;
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]);
296 return invalid_launch_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]);
311 * free the memory used by 'types'
313 static void free_type_list(struct type_list *types)
315 while (types != NULL) {
316 struct type_list *next = types->next;
323 * reads the configuration file handled by 'cread'
324 * and adds its contents to the launcher list
326 * returns 0 in case of success or -1 in case of error.
328 static int read_launchers(struct confread *cread)
331 struct type_list *types, *lt;
332 struct desc_launcher *desc;
333 enum afm_launch_mode mode;
340 mode = invalid_launch_mode;
341 rc = read_line(cread);
343 if (cread->index == 0) {
344 if (cread->length == 4
345 && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
346 /* check if allowed */
348 ERROR("%s:%d: mode found before"
353 free_type_list(types);
358 mode = read_mode(cread);
359 if (mode == invalid_launch_mode)
362 if (mode == invalid_launch_mode) {
363 ERROR("%s:%d: mode not found"
368 assert(types == NULL);
372 lt = read_type(cread);
374 free_type_list(types);
381 } else if (types == NULL && desc == NULL) {
383 ERROR("%s:%d: untyped launch vector found",
384 cread->filepath, cread->lineno);
386 ERROR("%s:%d: extra launch vector found"
387 " (the maximum count is 2)",
388 cread->filepath, cread->lineno);
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);
403 assert(desc == NULL);
404 desc = malloc(sizeof * desc);
406 ERROR("%s:%d: out of memory",
407 cread->filepath, cread->lineno);
408 free_type_list(types);
412 desc->next = launchers;
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;
422 desc->execs[1].has_readyfd = has_readyfd;
423 desc->execs[1].args = vector;
427 rc = read_line(cread);
430 ERROR("%s:%d: end of file found before launch vector",
431 cread->filepath, cread->lineno);
432 free_type_list(types);
440 * reads the configuration file 'filepath'
441 * and adds its contents to the launcher list
443 * returns 0 in case of success or -1 in case of error.
445 static int read_configuration_file(const char *filepath)
448 struct confread cread;
450 /* opens the configuration file */
451 cread.file = fopen(filepath, "r");
452 if (cread.file == NULL) {
454 ERROR("can't read file %s: %m", filepath);
458 cread.filepath = filepath;
460 rc = read_launchers(&cread);
467 * Creates a secret in 'buffer'
469 static void mksecret(char buffer[9])
471 snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
475 * Allocates a port and return it.
479 static int port_ring = 12345;
480 int port = port_ring;
481 if (port < 12345 || port > 15432)
483 port_ring = port + 1;
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
499 %r rootdir desc->path
500 %R readyfd params->readyfd
501 %S secret params->secret
506 * Union for handling either scalar arguments or vectorial arguments.
509 char *scalar; /* string of space separated arguments */
510 char **vector; /* vector of arguments */
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'.
520 * The vectors are made of an array pointers terminated by
523 * Returns the resulting value or NULL in case of error
525 static union arguments instantiate_arguments(
526 const char * const *args,
527 struct afm_launch_desc *desc,
528 struct launchparam *params,
532 const char * const *iter;
536 union arguments result;
537 char port[20], width[20], height[20], readyfd[20], mini[3];
540 sep = wants_vector ? 0 : ' ';
545 * loop that either compute the size and build the result
546 * advantage: appears only in one place
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 */
553 /* iterate over arguments */
554 for (n = 0, iter = args ; (p = *iter) != NULL ; iter++) {
555 /* init the vector */
557 result.vector[n] = data;
560 /* scan the argument */
561 while((c = *p++) != 0) {
563 /* standard character */
570 /* (convert num->string only once) */
573 case 'a': v = desc->appid; break;
575 v = "" /*TODO:desc->bindings*/;
577 case 'c': v = desc->content; break;
578 case 'D': v = params->datadir; break;
581 sprintf(height, "%d",
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;
597 sprintf(readyfd, "%d",
601 case 'r': v = desc->path; break;
602 case 'S': v = params->secret; break;
617 data = stpcpy(data, v);
619 s += (unsigned)strlen(v);
622 /* terminate the argument */
629 /* first iteration: allocation */
631 result.scalar = malloc(s);
632 data = result.scalar;
634 result.vector = malloc((n+1)*sizeof(char*) + s);
635 if (result.vector != NULL)
636 data = (char*)(&result.vector[n + 1]);
643 /* second iteration: termination */
647 result.vector[n] = NULL;
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'.
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.
662 * Returns the pid of the created process or -1 in case of error.
665 struct afm_launch_desc *desc,
666 struct launchparam *params,
667 struct exec_vector *exec,
677 /* prepare the pipes */
680 ERROR("error while calling pipe2: %m");
684 /* instanciate the arguments */
685 params->readyfd = rpipe[1];
686 args = instantiate_arguments(exec->args, desc, params, 1).vector;
690 ERROR("out of memory in master");
695 /* fork the master child */
699 /********* can't fork ************/
704 ERROR("master fork failed: %m");
709 /********* in the parent process ************/
716 /* wait for readyness */
717 poll(&pfd, 1, ready_timeout);
722 /********* in the child process ************/
726 /* avoid set-gid effect */
727 setresgid(groupid, groupid, groupid);
729 /* enter the process group */
730 rc = setpgid(0, progrp);
732 ERROR("setpgid failed");
736 /* enter security mode */
737 rc = secmgr_prepare_exec(desc->appid);
739 ERROR("call to secmgr_prepare_exec failed: %m");
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);
749 rc = chdir(params->datadir);
751 ERROR("can't enter the datadir %s: %m", params->datadir);
755 /* signal if needed */
756 if (!exec->has_readyfd) {
757 write(rpipe[1], readystr, sizeof(readystr) - 1);
761 /* executes the process */
762 rc = execve(args[0], args, environ);
763 ERROR("failed to exec master %s: %m", args[0]);
769 * Launches the application 'desc' in local mode using
770 * 'params' and store the resulting pids in 'children'.
772 * Returns 0 in case of success or -1 in case of error.
774 static int launch_local(
775 struct afm_launch_desc *desc,
777 struct launchparam *params
780 /* launches the first, making it group leader */
781 children[0] = launch(desc, params, ¶ms->execs[0], 0);
782 if (children[0] <= 0)
785 /* nothing more to launch ? */
786 if (params->execs[1].args == NULL)
789 /* launches the second in the group of the first */
790 children[1] = launch(desc, params, ¶ms->execs[1], children[0]);
794 /* kill all on error */
795 killpg(children[0], SIGKILL);
800 * Launches the application 'desc' in remote mode using
801 * 'params' and store the resulting pids in 'children'.
803 * Returns 0 in case of success or -1 in case of error.
805 static int launch_remote(
806 struct afm_launch_desc *desc,
808 struct launchparam *params
813 /* instanciate the uri */
814 if (params->execs[1].args == NULL)
817 uri = instantiate_arguments(params->execs[1].args, desc,
820 ERROR("out of memory for remote uri");
825 /* launch the command */
826 children[0] = launch(desc, params, ¶ms->execs[0], 0);
827 if (children[0] <= 0) {
832 /* returns the uri in params */
838 * Searchs the launcher descritpion for the given 'type' and 'mode'
840 * Returns the description found or NULL if nothing matches.
842 static struct desc_launcher *search_launcher(const char *type,
843 enum afm_launch_mode mode)
845 struct desc_launcher *dl;
846 struct type_list *tl;
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))
857 * Launches the application described by 'desc'
858 * and, in case of success, returns the resulting data
859 * in 'children' and 'uri'.
861 * Returns 0 in case of success or -1 in case of error.
863 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
866 char datadir[PATH_MAX];
868 struct launchparam params;
870 struct desc_launcher *dl;
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);
882 /* what launcher ? */
883 type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
884 dl = search_launcher(type, desc->mode);
886 ERROR("launcher not found for type %s and mode %s!",
887 type, name_of_launch_mode(desc->mode));
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");
901 /* make the secret and port */
904 params.port = mkport();
905 params.secret = secret;
906 params.datadir = datadir;
907 params.execs = dl->execs;
909 switch (desc->mode) {
911 return launch_local(desc, children, ¶ms);
913 return launch_remote(desc, children, ¶ms);
921 * Initialise the module
923 * Returns 0 on success or else -1 in case of failure
925 int afm_launch_initialize()
930 /* compute the groupid to set at launch */
931 getresgid(&r, &e, &s);
933 groupid = s; /* the original groupid is used */
937 rc = read_configuration_file(FWK_LAUNCH_CONF);
938 /* dump_launchers(stderr); */