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 + strspn(&cread->buffer[idx], separators);
145 cread->length = 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 = strspn(cread->buffer, separators);
160 if (cread->buffer[cread->index]
161 && cread->buffer[cread->index] != '#') {
162 cread->length = strcspn(&cread->buffer[cread->index],
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)
189 index0 = cread->index;
190 length0 = cread->length;
195 while(cread->length) {
197 length += 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], cread->length);
214 args += cread->length;
218 vector[count] = NULL;
219 cread->index = index0;
220 cread->length = length0;
225 * Reads the type from 'cread' directly in the list item and return it.
226 * returns NULL in case or error.
228 * - EINVAL extra characters
229 * - ENOMEM memory depletion
231 static struct type_list *read_type(struct confread *cread)
234 struct type_list *result;
236 /* record index and length */
237 index = cread->index;
238 length = cread->length;
240 /* check no extra characters */
241 if (next_token(cread)) {
242 ERROR("%s:%d: extra characters found after type %.*s",
243 cread->filepath, cread->lineno, length,
244 &cread->buffer[index]);
249 /* allocate structure */
250 result = malloc(sizeof(struct type_list) + length);
251 if (result == NULL) {
252 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
257 /* fill the structure */
258 memcpy(result->type, &cread->buffer[index], length);
259 result->type[length] = 0;
264 * Reads the mode from 'cread' and return it.
265 * returns invalid_launch_mode in case or error.
267 * - EINVAL no mode or extra characters or invalid mode
269 static enum afm_launch_mode read_mode(struct confread *cread)
272 enum afm_launch_mode result;
274 assert(cread->index == 0);
275 assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
277 /* get the next token: the mode string */
278 if (!next_token(cread)) {
279 ERROR("%s:%d: no mode value set", cread->filepath,
282 return invalid_launch_mode;
285 /* record index and length */
286 index = cread->index;
287 length = cread->length;
289 /* check no extra characters */
290 if (next_token(cread)) {
291 ERROR("%s:%d: extra characters found after mode %.*s",
292 cread->filepath, cread->lineno, length,
293 &cread->buffer[index]);
295 return invalid_launch_mode;
299 cread->buffer[index + length] = 0;
300 result = launch_mode_of_name(&cread->buffer[index]);
301 if (result == invalid_launch_mode) {
302 ERROR("%s:%d: invalid mode value %s",
303 cread->filepath, cread->lineno, &cread->buffer[index]);
310 * free the memory used by 'types'
312 static void free_type_list(struct type_list *types)
314 while (types != NULL) {
315 struct type_list *next = types->next;
322 * reads the configuration file handled by 'cread'
323 * and adds its contents to the launcher list
325 * returns 0 in case of success or -1 in case of error.
327 static int read_launchers(struct confread *cread)
330 struct type_list *types, *lt;
331 struct desc_launcher *desc;
332 enum afm_launch_mode mode;
339 mode = invalid_launch_mode;
340 rc = read_line(cread);
342 if (cread->index == 0) {
343 if (cread->length == 4
344 && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
345 /* check if allowed */
347 ERROR("%s:%d: mode found before"
352 free_type_list(types);
357 mode = read_mode(cread);
358 if (mode == invalid_launch_mode)
361 if (mode == invalid_launch_mode) {
362 ERROR("%s:%d: mode not found"
367 assert(types == NULL);
371 lt = read_type(cread);
373 free_type_list(types);
380 } else if (types == NULL && desc == NULL) {
382 ERROR("%s:%d: untyped launch vector found",
383 cread->filepath, cread->lineno);
385 ERROR("%s:%d: extra launch vector found"
386 " (the maximum count is 2)",
387 cread->filepath, cread->lineno);
391 has_readyfd = NULL != strstr(
392 &cread->buffer[cread->index], "%R");
393 vector = read_vector(cread);
394 if (vector == NULL) {
395 ERROR("%s:%d: out of memory",
396 cread->filepath, cread->lineno);
397 free_type_list(types);
402 assert(desc == NULL);
403 desc = malloc(sizeof * desc);
405 ERROR("%s:%d: out of memory",
406 cread->filepath, cread->lineno);
407 free_type_list(types);
411 desc->next = launchers;
414 desc->execs[0].has_readyfd = has_readyfd;
415 desc->execs[0].args = vector;
416 desc->execs[1].has_readyfd = 0;
417 desc->execs[1].args = NULL;
421 desc->execs[1].has_readyfd = has_readyfd;
422 desc->execs[1].args = vector;
426 rc = read_line(cread);
429 ERROR("%s:%d: end of file found before launch vector",
430 cread->filepath, cread->lineno);
431 free_type_list(types);
439 * reads the configuration file 'filepath'
440 * and adds its contents to the launcher list
442 * returns 0 in case of success or -1 in case of error.
444 static int read_configuration_file(const char *filepath)
447 struct confread cread;
449 /* opens the configuration file */
450 cread.file = fopen(filepath, "r");
451 if (cread.file == NULL) {
453 ERROR("can't read file %s: %m", filepath);
457 cread.filepath = filepath;
459 rc = read_launchers(&cread);
466 * Creates a secret in 'buffer'
468 static void mksecret(char buffer[9])
470 snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
474 * Allocates a port and return it.
478 static int port_ring = 12345;
479 int port = port_ring;
480 if (port < 12345 || port > 15432)
482 port_ring = port + 1;
489 %c content desc->content
490 %D datadir params->datadir
491 %H height desc->height
492 %h homedir desc->home
493 %I icondir FWK_ICON_DIR
494 %m mime-type desc->type
496 %p plugins desc->plugins
498 %r rootdir desc->path
499 %R readyfd params->readyfd
500 %S secret params->secret
505 * Union for handling either scalar arguments or vectorial arguments.
508 char *scalar; /* string of space separated arguments */
509 char **vector; /* vector of arguments */
513 * Computes the substitutions of 'args' according to the
514 * data of 'desc' and 'params'. The result is a single
515 * piece of memory (that must be freed in one time)
516 * containing either a vector or a string depending on
517 * the value of 'wants_vector'.
519 * The vectors are made of an array pointers terminated by
522 * Returns the resulting value or NULL in case of error
524 static union arguments instantiate_arguments(
525 const char * const *args,
526 struct afm_launch_desc *desc,
527 struct launchparam *params,
531 const char * const *iter;
535 union arguments result;
536 char port[20], width[20], height[20], readyfd[20], mini[3];
539 sep = wants_vector ? 0 : ' ';
544 * loop that either compute the size and build the result
545 * advantage: appears only in one place
547 result.vector = NULL; /* initialise both to avoid */
548 result.scalar = NULL; /* a very stupid compiler warning */
549 data = NULL; /* no data for the first iteration */
552 /* iterate over arguments */
553 for (n = 0, iter = args ; (p = *iter) != NULL ; iter++) {
554 /* init the vector */
556 result.vector[n] = data;
559 /* scan the argument */
560 while((c = *p++) != 0) {
562 /* standard character */
569 /* (convert num->string only once) */
572 case 'a': v = desc->appid; break;
573 case 'c': v = desc->content; break;
574 case 'D': v = params->datadir; break;
577 sprintf(height, "%d",
581 case 'h': v = desc->home; break;
582 case 'I': v = FWK_ICON_DIR; break;
583 case 'm': v = desc->type; break;
584 case 'n': v = desc->name; break;
592 v = "" /*TODO:desc->plugins*/;
596 sprintf(readyfd, "%d",
600 case 'r': v = desc->path; break;
601 case 'S': v = params->secret; break;
616 data = stpcpy(data, v);
621 /* terminate the argument */
628 /* first iteration: allocation */
630 result.scalar = malloc(s);
631 data = result.scalar;
633 result.vector = malloc((n+1)*sizeof(char*) + s);
634 if (result.vector != NULL)
635 data = (char*)(&result.vector[n + 1]);
642 /* second iteration: termination */
646 result.vector[n] = NULL;
653 * Launchs (fork-execs) the program described by 'exec'
654 * using the parameters of 'desc' and 'params' to instantiate
655 * it. The created process is attached to the process group 'progrp'.
657 * After being created and before to be launched, the process
658 * is put in its security environment and its directory is
659 * changed to params->datadir.
661 * Returns the pid of the created process or -1 in case of error.
664 struct afm_launch_desc *desc,
665 struct launchparam *params,
666 struct exec_vector *exec,
676 /* prepare the pipes */
679 ERROR("error while calling pipe2: %m");
683 /* instanciate the arguments */
684 params->readyfd = rpipe[1];
685 args = instantiate_arguments(exec->args, desc, params, 1).vector;
689 ERROR("out of memory in master");
694 /* fork the master child */
698 /********* can't fork ************/
703 ERROR("master fork failed: %m");
708 /********* in the parent process ************/
715 /* wait for readyness */
716 poll(&pfd, 1, ready_timeout);
721 /********* in the child process ************/
725 /* avoid set-gid effect */
726 setresgid(groupid, groupid, groupid);
728 /* enter the process group */
729 rc = setpgid(0, progrp);
731 ERROR("setpgid failed");
735 /* enter security mode */
736 rc = secmgr_prepare_exec(desc->appid);
738 ERROR("call to secmgr_prepare_exec failed: %m");
742 /* enter the datadirectory */
743 rc = mkdir(params->datadir, 0755);
744 if (rc && errno != EEXIST) {
745 ERROR("creation of datadir %s failed: %m", params->datadir);
748 rc = chdir(params->datadir);
750 ERROR("can't enter the datadir %s: %m", params->datadir);
754 /* signal if needed */
755 if (!exec->has_readyfd) {
756 write(rpipe[1], readystr, sizeof(readystr) - 1);
760 /* executes the process */
761 rc = execve(args[0], args, environ);
762 ERROR("failed to exec master %s: %m", args[0]);
768 * Launches the application 'desc' in local mode using
769 * 'params' and store the resulting pids in 'children'.
771 * Returns 0 in case of success or -1 in case of error.
773 static int launch_local(
774 struct afm_launch_desc *desc,
776 struct launchparam *params
779 /* launches the first, making it group leader */
780 children[0] = launch(desc, params, ¶ms->execs[0], 0);
781 if (children[0] <= 0)
784 /* nothing more to launch ? */
785 if (params->execs[1].args == NULL)
788 /* launches the second in the group of the first */
789 children[1] = launch(desc, params, ¶ms->execs[1], children[0]);
793 /* kill all on error */
794 killpg(children[0], SIGKILL);
799 * Launches the application 'desc' in remote mode using
800 * 'params' and store the resulting pids in 'children'.
802 * Returns 0 in case of success or -1 in case of error.
804 static int launch_remote(
805 struct afm_launch_desc *desc,
807 struct launchparam *params
812 /* instanciate the uri */
813 if (params->execs[1].args == NULL)
816 uri = instantiate_arguments(params->execs[1].args, desc,
819 ERROR("out of memory for remote uri");
824 /* launch the command */
825 children[0] = launch(desc, params, ¶ms->execs[0], 0);
826 if (children[0] <= 0) {
831 /* returns the uri in params */
837 * Searchs the launcher descritpion for the given 'type' and 'mode'
839 * Returns the description found or NULL if nothing matches.
841 static struct desc_launcher *search_launcher(const char *type,
842 enum afm_launch_mode mode)
844 struct desc_launcher *dl;
845 struct type_list *tl;
847 for (dl = launchers ; dl ; dl = dl->next)
848 if (dl->mode == mode)
849 for (tl = dl->types ; tl != NULL ; tl = tl->next)
850 if (!strcmp(tl->type, type))
856 * Launches the application described by 'desc'
857 * and, in case of success, returns the resulting data
858 * in 'children' and 'uri'.
860 * Returns 0 in case of success or -1 in case of error.
862 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
865 char datadir[PATH_MAX];
867 struct launchparam params;
869 struct desc_launcher *dl;
872 assert(groupid != 0);
873 assert(is_valid_launch_mode(desc->mode));
874 assert(desc->mode == mode_local || uri != NULL);
875 assert(uri == NULL || *uri == NULL);
881 /* what launcher ? */
882 type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
883 dl = search_launcher(type, desc->mode);
885 ERROR("launcher not found for type %s and mode %s!",
886 type, name_of_launch_mode(desc->mode));
892 rc = snprintf(datadir, sizeof datadir, "%s/%s",
893 desc->home, desc->appid);
894 if (rc < 0 || rc >= sizeof datadir) {
895 ERROR("overflow for datadir");
900 /* make the secret and port */
903 params.port = mkport();
904 params.secret = secret;
905 params.datadir = datadir;
906 params.execs = dl->execs;
908 switch (desc->mode) {
910 return launch_local(desc, children, ¶ms);
912 return launch_remote(desc, children, ¶ms);
920 * Initialise the module
922 * Returns 0 on success or else -1 in case of failure
924 int afm_launch_initialize()
929 /* compute the groupid to set at launch */
930 getresgid(&r, &e, &s);
932 groupid = s; /* the original groupid is used */
936 rc = read_configuration_file(FWK_LAUNCH_CONF);
937 /* dump_launchers(stderr); */