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>
32 extern char **environ;
35 #include "afm-launch-mode.h"
36 #include "afm-launch.h"
37 #include "secmgr-wrap.h"
39 #define DEFAULT_TYPE "text/html"
42 struct type_list *next;
47 struct desc_list *next;
48 enum afm_launch_mode mode;
49 struct type_list *types;
70 struct desc_list *launchers = NULL;
72 static gid_t groupid = 0;
74 const char separators[] = " \t\n";
76 static void dump_launchers()
79 struct desc_list *desc;
80 struct type_list *type;
82 for (desc = launchers ; desc != NULL ; desc = desc->next) {
83 printf("mode %s\n", name_of_launch_mode(desc->mode));
84 for (type = desc->types ; type != NULL ; type = type->next)
85 printf("%s\n", type->type);
86 for ( j = 0 ; j < 2 ; j++)
87 if (desc->execs[j] != NULL) {
88 for (k = 0 ; desc->execs[j][k] != NULL ; k++)
89 printf(" %s", desc->execs[j][k]);
96 static int next_token(struct confread *cread)
98 cread->index += cread->length + strspn(&cread->buffer[cread->index + cread->length], separators);
99 cread->length = strcspn(&cread->buffer[cread->index], separators);
100 return cread->length;
103 static int read_line(struct confread *cread)
105 while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
107 cread->index = strspn(cread->buffer, separators);
108 if (cread->buffer[cread->index] && cread->buffer[cread->index] != '#') {
109 cread->length = strcspn(&cread->buffer[cread->index], separators);
110 assert(cread->length > 0);
111 return cread->length;
114 if (ferror(cread->file)) {
115 ERROR("%s:%d: error while reading, %m", cread->filepath, cread->lineno);
121 static char **read_vector(struct confread *cread)
124 char **vector, *args;
128 index0 = cread->index;
129 length0 = cread->length;
134 while(cread->length) {
136 length += cread->length;
141 cread->index = index0;
142 cread->length = length0;
143 vector = malloc(length + count + (count + 1) * sizeof(char*));
148 args = (char*)(vector + count + 1);
150 while(cread->length) {
151 vector[count++] = args;
152 memcpy(args, &cread->buffer[cread->index], cread->length);
153 args += cread->length;
157 vector[count] = NULL;
158 cread->index = index0;
159 cread->length = length0;
163 static struct type_list *read_type(struct confread *cread)
166 struct type_list *result;
168 /* record index and length */
169 index = cread->index;
170 length = cread->length;
172 /* check no extra characters */
173 if (next_token(cread)) {
174 ERROR("%s:%d: extra characters found after type %.*s", cread->filepath, cread->lineno, length, &cread->buffer[index]);
179 /* allocate structure */
180 result = malloc(sizeof(struct type_list) + length);
181 if (result == NULL) {
182 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
187 /* fill the structure */
188 memcpy(result->type, &cread->buffer[index], length);
189 result->type[length] = 0;
193 static enum afm_launch_mode read_mode(struct confread *cread)
196 enum afm_launch_mode result;
198 assert(cread->index == 0);
199 assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
201 /* get the next token: the mode string */
202 if (!next_token(cread)) {
203 ERROR("%s:%d: no mode value set", cread->filepath, cread->lineno);
205 return invalid_launch_mode;
208 /* record index and length */
209 index = cread->index;
210 length = cread->length;
212 /* check no extra characters */
213 if (next_token(cread)) {
214 ERROR("%s:%d: extra characters found after mode %.*s", cread->filepath, cread->lineno, length, &cread->buffer[index]);
216 return invalid_launch_mode;
220 cread->buffer[index + length] = 0;
221 result = launch_mode_of_string(&cread->buffer[index]);
222 if (result == invalid_launch_mode) {
223 ERROR("%s:%d: invalid mode value %s", cread->filepath, cread->lineno, &cread->buffer[index]);
229 static void free_type_list(struct type_list *types)
231 while (types != NULL) {
232 struct type_list *next = types->next;
238 static int read_launchers(struct confread *cread)
241 struct type_list *types, *lt;
242 struct desc_list *desc;
243 enum afm_launch_mode mode;
250 mode = invalid_launch_mode;
251 rc = read_line(cread);
253 if (cread->index == 0) {
254 if (cread->length == 4 && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
255 /* check if allowed */
257 ERROR("%s:%d: mode found before launch vector", cread->filepath, cread->lineno);
259 free_type_list(types);
264 mode = read_mode(cread);
265 if (mode == invalid_launch_mode)
268 if (mode == invalid_launch_mode) {
269 ERROR("%s:%d: mode not found before type", cread->filepath, cread->lineno);
271 assert(types == NULL);
275 lt = read_type(cread);
277 free_type_list(types);
284 } else if (types == NULL && desc == NULL) {
286 ERROR("%s:%d: untyped launch vector found", cread->filepath, cread->lineno);
288 ERROR("%s:%d: extra launch vector found (2 max)", cread->filepath, cread->lineno);
292 vector = read_vector(cread);
293 if (vector == NULL) {
294 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
295 free_type_list(types);
300 assert(desc == NULL);
301 desc = malloc(sizeof * desc);
303 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
304 free_type_list(types);
308 desc->next = launchers;
311 desc->execs[0] = vector;
312 desc->execs[1] = NULL;
316 desc->execs[1] = vector;
320 rc = read_line(cread);
323 ERROR("%s:%d: end of file found before launch vector", cread->filepath, cread->lineno);
324 free_type_list(types);
331 static int read_configuration_file(const char *filepath)
334 struct confread cread;
336 /* opens the configuration file */
337 cread.file = fopen(filepath, "r");
338 if (cread.file == NULL) {
340 ERROR("can't read file %s: %m", filepath);
344 cread.filepath = filepath;
346 rc = read_launchers(&cread);
353 %I icondir FWK_ICON_DIR
355 %S secret params->secret
356 %D datadir params->datadir
357 %r rootdir desc->path
358 %h homedir desc->home
359 %t tag (smack label) desc->tag
361 %c content desc->content
362 %m mime-type desc->type
364 %p plugins desc->plugins
366 %H height desc->height
370 static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params)
372 const char **iter, *p, *v;
373 char *data, **result, port[20], width[20], height[20], mini[3], c;
380 /* loop that either compute the size and build the result */
392 while((c = *p++) != 0) {
401 case 'I': v = FWK_ICON_DIR; break;
402 case 'P': if(!data) sprintf(port, "%d", params->port); v = port; break;
403 case 'S': v = params->secret; break;
404 case 'D': v = params->datadir; break;
405 case 'r': v = desc->path; break;
406 case 'h': v = desc->home; break;
407 case 't': v = desc->tag; break;
408 case 'a': v = desc->appid; break;
409 case 'c': v = desc->content; break;
410 case 'm': v = desc->type; break;
411 case 'n': v = desc->name; break;
412 case 'p': v = "" /*desc->plugins*/; break;
413 case 'W': if(!data) sprintf(width, "%d", desc->width); v = width; break;
414 case 'H': if(!data) sprintf(height, "%d", desc->height); v = height; break;
416 default: mini[1] = c; v = mini; break;
419 data = stpcpy(data, v);
434 result = malloc((n+1)*sizeof(char*) + s);
435 if (result == NULL) {
439 data = (char*)(&result[n + 1]);
443 static void mksecret(char buffer[9])
445 snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
450 static int port_ring = 12345;
451 int port = port_ring;
452 if (port < 12345 || port > 15432)
454 port_ring = port + 1;
458 static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
463 /* fork the master child */
464 children[0] = fork();
465 if (children[0] < 0) {
466 ERROR("master fork failed: %m");
470 /********* in the parent process ************/
474 /********* in the master child ************/
476 /* avoid set-gid effect */
477 setresgid(groupid, groupid, groupid);
479 /* enter the process group */
482 ERROR("setpgid failed");
486 /* enter security mode */
487 rc = secmgr_prepare_exec(desc->tag);
489 ERROR("call to secmgr_prepare_exec failed: %m");
493 /* enter the datadirectory */
494 rc = mkdir(params->datadir, 0755);
495 if (rc && errno != EEXIST) {
496 ERROR("creation of datadir %s failed: %m", params->datadir);
499 rc = chdir(params->datadir);
501 ERROR("can't enter the datadir %s: %m", params->datadir);
505 args = instantiate_arguments(params->master, desc, params);
507 ERROR("out of memory in master");
510 rc = execve(args[0], args, environ);
511 ERROR("failed to exec master %s: %m", args[0]);
516 static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
524 /* prepare the pipes */
525 rc = pipe2(mpipe, O_CLOEXEC);
527 ERROR("error while calling pipe2: %m");
530 rc = pipe2(spipe, O_CLOEXEC);
532 ERROR("error while calling pipe2: %m");
538 /* fork the master child */
539 children[0] = fork();
540 if (children[0] < 0) {
541 ERROR("master fork failed: %m");
549 /********* in the parent process ************/
552 /* wait the ready signal (that transmit the slave pid) */
553 rc = read(mpipe[0], &children[1], sizeof children[1]);
556 ERROR("reading master pipe failed: %m");
560 assert(rc == sizeof children[1]);
561 /* start the child */
562 rc = write(spipe[1], "start", 5);
564 ERROR("writing slave pipe failed: %m");
573 /********* in the master child ************/
577 /* avoid set-gid effect */
578 setresgid(groupid, groupid, groupid);
580 /* enter the process group */
583 ERROR("setpgid failed");
587 /* enter security mode */
588 rc = secmgr_prepare_exec(desc->tag);
590 ERROR("call to secmgr_prepare_exec failed: %m");
594 /* enter the datadirectory */
595 rc = mkdir(params->datadir, 0755);
596 if (rc && errno != EEXIST) {
597 ERROR("creation of datadir %s failed: %m", params->datadir);
600 rc = chdir(params->datadir);
602 ERROR("can't enter the datadir %s: %m", params->datadir);
606 /* fork the slave child */
607 children[1] = fork();
608 if (children[1] < 0) {
609 ERROR("slave fork failed: %m");
612 if (children[1] == 0) {
613 /********* in the slave child ************/
615 rc = read(spipe[0], message, sizeof message);
617 ERROR("reading slave pipe failed: %m");
621 args = instantiate_arguments(params->slave, desc, params);
623 ERROR("out of memory in slave");
626 rc = execve(args[0], args, environ);
627 ERROR("failed to exec slave %s: %m", args[0]);
632 /********* still in the master child ************/
634 args = instantiate_arguments(params->master, desc, params);
636 ERROR("out of memory in master");
639 rc = write(mpipe[1], &children[1], sizeof children[1]);
641 ERROR("can't write master pipe: %m");
645 rc = execve(args[0], args, environ);
646 ERROR("failed to exec master %s: %m", args[0]);
652 int afm_launch_initialize()
657 getresgid(&r, &e, &s);
663 rc = read_configuration_file(FWK_LAUNCH_CONF);
668 static struct desc_list *search_launcher(const char *type, enum afm_launch_mode mode)
670 struct desc_list *dl;
671 struct type_list *tl;
673 for (dl = launchers ; dl ; dl = dl->next)
674 if (dl->mode == mode)
675 for (tl = dl->types ; tl != NULL ; tl = tl->next)
676 if (!strcmp(tl->type, type))
681 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
683 char datadir[PATH_MAX];
686 struct launchparam params;
688 struct desc_list *dl;
691 assert(groupid != 0);
692 assert(launch_mode_is_valid(desc->mode));
698 /* what launcher ? */
699 type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
700 dl = search_launcher(type, desc->mode);
702 ERROR("type %s not found for mode %s!", type, name_of_launch_mode(desc->mode));
708 rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
709 if (rc < 0 || rc >= sizeof datadir) {
710 ERROR("overflow for datadir");
715 /* make the secret and port */
717 params.port = mkport();
718 params.secret = secret;
719 params.datadir = datadir;
720 params.master = (const char **)dl->execs[0];
721 params.slave = (const char **)dl->execs[1];
723 return params.slave ? launchexec2(desc, children, ¶ms) : launchexec1(desc, children, ¶ms);