87e8e3a6c734eba540b3e7a661cb90df19217ed3
[src/app-framework-main.git] / src / afm-launch.c
1 /*
2  Copyright 2015 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
32 extern char **environ;
33
34 #include "verbose.h"
35 #include "afm-launch-mode.h"
36 #include "afm-launch.h"
37 #include "secmgr-wrap.h"
38
39 #define DEFAULT_TYPE "text/html"
40
41 struct type_list {
42         struct type_list *next;
43         char type[1];
44 };
45
46 struct desc_list {
47         struct desc_list *next;
48         enum afm_launch_mode mode;
49         struct type_list *types;
50         char **execs[2];
51 };
52
53 struct launchparam {
54         int port;
55         const char *secret;
56         const char *datadir;
57         const char **master;
58         const char **slave;
59 };
60
61 struct confread {
62         const char *filepath;
63         FILE *file;
64         int lineno;
65         int index;
66         int length;
67         char buffer[4096];
68 };
69
70 struct desc_list *launchers = NULL;
71
72 static gid_t groupid = 0;
73
74 const char separators[] = " \t\n";
75
76 static void dump_launchers()
77 {
78         int j, k;
79         struct desc_list *desc;
80         struct type_list *type;
81
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]);
90                                 printf("\n");
91                         }
92                 printf("\n");
93         }
94 }
95
96 static int next_token(struct confread *cread)
97 {
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;
101 }
102
103 static int read_line(struct confread *cread)
104 {
105         while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
106                 cread->lineno++;
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;
112                 }
113         }
114         if (ferror(cread->file)) {
115                 ERROR("%s:%d: error while reading, %m", cread->filepath, cread->lineno);
116                 return -1;
117         }
118         return 0;
119 }
120
121 static char **read_vector(struct confread *cread)
122 {
123         int index0, length0;
124         char **vector, *args;
125         int count, length;
126
127         /* record origin */
128         index0 = cread->index;
129         length0 = cread->length;
130
131         /* count */
132         count = 0;
133         length = 0;
134         while(cread->length) {
135                 count++;
136                 length += cread->length;
137                 next_token(cread);
138         }
139
140         /* allocates */
141         cread->index = index0;
142         cread->length = length0;
143         vector = malloc(length + count + (count + 1) * sizeof(char*));
144         if (vector == NULL)
145                 return NULL;
146
147         /* copies */
148         args = (char*)(vector + count + 1);
149         count = 0;
150         while(cread->length) {
151                 vector[count++] = args;
152                 memcpy(args, &cread->buffer[cread->index], cread->length);
153                 args += cread->length;
154                 *args++ = 0;
155                 next_token(cread);
156         }
157         vector[count] = NULL;
158         cread->index = index0;
159         cread->length = length0;
160         return vector;
161 }
162
163 static struct type_list *read_type(struct confread *cread)
164 {
165         int index, length;
166         struct type_list *result;
167
168         /* record index and length */
169         index = cread->index;
170         length = cread->length;
171
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]);
175                 errno = EINVAL;
176                 return NULL;
177         }
178
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);
183                 errno = ENOMEM;
184                 return NULL;
185         }
186
187         /* fill the structure */
188         memcpy(result->type, &cread->buffer[index], length);
189         result->type[length] = 0;
190         return result;
191 }
192
193 static enum afm_launch_mode read_mode(struct confread *cread)
194 {
195         int index, length;
196         enum afm_launch_mode result;
197
198         assert(cread->index == 0);
199         assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
200
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);
204                 errno = EINVAL;
205                 return invalid_launch_mode;
206         }
207
208         /* record index and length */
209         index = cread->index;
210         length = cread->length;
211
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]);
215                 errno = EINVAL;
216                 return invalid_launch_mode;
217         }
218
219         /* get the 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]);
224                 errno = EINVAL;
225         }
226         return result;
227 }
228
229 static void free_type_list(struct type_list *types)
230 {
231         while (types != NULL) {
232                 struct type_list *next = types->next;
233                 free(types);
234                 types = next;
235         }
236 }
237
238 static int read_launchers(struct confread *cread)
239 {
240         int rc;
241         struct type_list *types, *lt;
242         struct desc_list *desc;
243         enum afm_launch_mode mode;
244         char **vector;
245
246         /* reads the file */
247         lt = NULL;
248         types = NULL;
249         desc = NULL;
250         mode = invalid_launch_mode;
251         rc = read_line(cread);
252         while (rc > 0) {
253                 if (cread->index == 0) {
254                         if (cread->length == 4 && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
255                                 /* check if allowed */
256                                 if (types != NULL) {
257                                         ERROR("%s:%d: mode found before launch vector", cread->filepath, cread->lineno);
258                                         errno = EINVAL;
259                                         free_type_list(types);
260                                         return -1;
261                                 }
262
263                                 /* read the mode */
264                                 mode = read_mode(cread);
265                                 if (mode == invalid_launch_mode)
266                                         return -1;
267                         } else {
268                                 if (mode == invalid_launch_mode) {
269                                         ERROR("%s:%d: mode not found before type", cread->filepath, cread->lineno);
270                                         errno = EINVAL;
271                                         assert(types == NULL);
272                                         return -1;
273                                 }
274                                 /* read a type */
275                                 lt = read_type(cread);
276                                 if (lt == NULL) {
277                                         free_type_list(types);
278                                         return -1;
279                                 }
280                                 lt->next = types;
281                                 types = lt;
282                         }
283                         desc = NULL;
284                 } else if (types == NULL && desc == NULL) {
285                         if (lt == NULL)
286                                 ERROR("%s:%d: untyped launch vector found", cread->filepath, cread->lineno);
287                         else
288                                 ERROR("%s:%d: extra launch vector found (2 max)", cread->filepath, cread->lineno);
289                         errno = EINVAL;
290                         return -1;
291                 } else {
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);
296                                 errno = ENOMEM;
297                                 return -1;
298                         }
299                         if (types) {
300                                 assert(desc == NULL);
301                                 desc = malloc(sizeof * desc);
302                                 if (desc == NULL) {
303                                         ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
304                                         free_type_list(types);
305                                         errno = ENOMEM;
306                                         return -1;
307                                 }
308                                 desc->next = launchers;
309                                 desc->mode = mode;
310                                 desc->types = types;
311                                 desc->execs[0] = vector;
312                                 desc->execs[1] = NULL;
313                                 types = NULL;
314                                 launchers = desc;
315                         } else {
316                                 desc->execs[1] = vector;
317                                 desc = NULL;
318                         }
319                 }
320                 rc = read_line(cread);
321         }
322         if (types != NULL) {
323                 ERROR("%s:%d: end of file found before launch vector", cread->filepath, cread->lineno);
324                 free_type_list(types);
325                 errno = EINVAL;
326                 return -1;
327         }
328         return rc;
329 }
330
331 static int read_configuration_file(const char *filepath)
332 {
333         int rc;
334         struct confread cread;
335
336         /* opens the configuration file */
337         cread.file = fopen(filepath, "r");
338         if (cread.file == NULL) {
339                 /* error */
340                 ERROR("can't read file %s: %m", filepath);
341                 rc = -1;
342         } else {
343                 /* reads it */
344                 cread.filepath = filepath;
345                 cread.lineno = 0;
346                 rc = read_launchers(&cread);
347                 fclose(cread.file);
348         }
349         return rc;
350 }
351
352 /*
353 %I icondir                      FWK_ICON_DIR
354 %P port                         params->port
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
360 %a appid                        desc->appid
361 %c content                      desc->content
362 %m mime-type                    desc->type
363 %n name                         desc->name
364 %p plugins                      desc->plugins
365 %W width                        desc->width
366 %H height                       desc->height
367 %% %
368 */
369
370 static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params)
371 {
372         const char **iter, *p, *v;
373         char *data, **result, port[20], width[20], height[20], mini[3], c;
374         int n, s;
375
376         /* init */
377         mini[0] = '%';
378         mini[2] = 0;
379
380         /* loop that either compute the size and build the result */
381         data = NULL;
382         result = NULL;
383         n = s = 0;
384         for (;;) {
385                 iter = args;
386                 n = 0;
387                 while (*iter) {
388                         p = *iter++;
389                         if (data)
390                                 result[n] = data;
391                         n++;
392                         while((c = *p++) != 0) {
393                                 if (c != '%') {
394                                         if (data)
395                                                 *data++ = c;
396                                         else
397                                                 s++;
398                                 } else {
399                                         c = *p++;
400                                         switch (c) {
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;
415                                         case '%': c = 0;
416                                         default: mini[1] = c; v = mini; break;
417                                         }
418                                         if (data)
419                                                 data = stpcpy(data, v);
420                                         else
421                                                 s += strlen(v);
422                                 }
423                         }
424                         if (data)
425                                 *data++ = 0;
426                         else
427                                 s++;
428                 }
429                 if (data) {
430                         result[n] = NULL;
431                         return result;
432                 }
433                 /* allocation */
434                 result = malloc((n+1)*sizeof(char*) + s);
435                 if (result == NULL) {
436                         errno = ENOMEM;
437                         return NULL;
438                 }
439                 data = (char*)(&result[n + 1]);
440         }
441 }
442
443 static void mksecret(char buffer[9])
444 {
445         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
446 }
447
448 static int mkport()
449 {
450         static int port_ring = 12345;
451         int port = port_ring;
452         if (port < 12345 || port > 15432)
453                 port = 12345;
454         port_ring = port + 1;
455         return port;
456 }
457
458 static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
459 {
460         int rc;
461         char **args;
462
463         /* fork the master child */
464         children[0] = fork();
465         if (children[0] < 0) {
466                 ERROR("master fork failed: %m");
467                 return -1;
468         }
469         if (children[0]) {
470                 /********* in the parent process ************/
471                 return 0;
472         }
473
474         /********* in the master child ************/
475
476         /* avoid set-gid effect */
477         setresgid(groupid, groupid, groupid);
478
479         /* enter the process group */
480         rc = setpgid(0, 0);
481         if (rc) {
482                 ERROR("setpgid failed");
483                 _exit(1);
484         }
485
486         /* enter security mode */
487         rc = secmgr_prepare_exec(desc->tag);
488         if (rc < 0) {
489                 ERROR("call to secmgr_prepare_exec failed: %m");
490                 _exit(1);
491         }
492
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);
497                 _exit(1);
498         }
499         rc = chdir(params->datadir);
500         if (rc) {
501                 ERROR("can't enter the datadir %s: %m", params->datadir);
502                 _exit(1);
503         }
504
505         args = instantiate_arguments(params->master, desc, params);
506         if (args == NULL) {
507                 ERROR("out of memory in master");
508         }
509         else {
510                 rc = execve(args[0], args, environ);
511                 ERROR("failed to exec master %s: %m", args[0]);
512         }
513         _exit(1);
514 }
515
516 static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
517 {
518         int rc;
519         char message[10];
520         int mpipe[2];
521         int spipe[2];
522         char **args;
523
524         /* prepare the pipes */
525         rc = pipe2(mpipe, O_CLOEXEC);
526         if (rc < 0) {
527                 ERROR("error while calling pipe2: %m");
528                 return -1;
529         }
530         rc = pipe2(spipe, O_CLOEXEC);
531         if (rc < 0) {
532                 ERROR("error while calling pipe2: %m");
533                 close(spipe[0]);
534                 close(spipe[1]);
535                 return -1;
536         }
537
538         /* fork the master child */
539         children[0] = fork();
540         if (children[0] < 0) {
541                 ERROR("master fork failed: %m");
542                 close(mpipe[0]);
543                 close(mpipe[1]);
544                 close(spipe[0]);
545                 close(spipe[1]);
546                 return -1;
547         }
548         if (children[0]) {
549                 /********* in the parent process ************/
550                 close(mpipe[1]);
551                 close(spipe[0]);
552                 /* wait the ready signal (that transmit the slave pid) */
553                 rc = read(mpipe[0], &children[1], sizeof children[1]);
554                 close(mpipe[0]);
555                 if (rc  <= 0) {
556                         ERROR("reading master pipe failed: %m");
557                         close(spipe[1]);
558                         return -1;
559                 }
560                 assert(rc == sizeof children[1]);
561                 /* start the child */
562                 rc = write(spipe[1], "start", 5);
563                 if (rc < 0) {
564                         ERROR("writing slave pipe failed: %m");
565                         close(spipe[1]);
566                         return -1;
567                 }
568                 assert(rc == 5);
569                 close(spipe[1]);
570                 return 0;
571         }
572
573         /********* in the master child ************/
574         close(mpipe[0]);
575         close(spipe[1]);
576
577         /* avoid set-gid effect */
578         setresgid(groupid, groupid, groupid);
579
580         /* enter the process group */
581         rc = setpgid(0, 0);
582         if (rc) {
583                 ERROR("setpgid failed");
584                 _exit(1);
585         }
586
587         /* enter security mode */
588         rc = secmgr_prepare_exec(desc->tag);
589         if (rc < 0) {
590                 ERROR("call to secmgr_prepare_exec failed: %m");
591                 _exit(1);
592         }
593
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);
598                 _exit(1);
599         }
600         rc = chdir(params->datadir);
601         if (rc) {
602                 ERROR("can't enter the datadir %s: %m", params->datadir);
603                 _exit(1);
604         }
605
606         /* fork the slave child */
607         children[1] = fork();
608         if (children[1] < 0) {
609                 ERROR("slave fork failed: %m");
610                 _exit(1);
611         }
612         if (children[1] == 0) {
613                 /********* in the slave child ************/
614                 close(mpipe[0]);
615                 rc = read(spipe[0], message, sizeof message);
616                 if (rc <= 0) {
617                         ERROR("reading slave pipe failed: %m");
618                         _exit(1);
619                 }
620
621                 args = instantiate_arguments(params->slave, desc, params);
622                 if (args == NULL) {
623                         ERROR("out of memory in slave");
624                 }
625                 else {
626                         rc = execve(args[0], args, environ);
627                         ERROR("failed to exec slave %s: %m", args[0]);
628                 }
629                 _exit(1);
630         }
631
632         /********* still in the master child ************/
633         close(spipe[1]);
634         args = instantiate_arguments(params->master, desc, params);
635         if (args == NULL) {
636                 ERROR("out of memory in master");
637         }
638         else {
639                 rc = write(mpipe[1], &children[1], sizeof children[1]);
640                 if (rc <= 0) {
641                         ERROR("can't write master pipe: %m");
642                 }
643                 else {
644                         close(mpipe[1]);
645                         rc = execve(args[0], args, environ);
646                         ERROR("failed to exec master %s: %m", args[0]);
647                 }
648         }
649         _exit(1);
650 }
651
652 int afm_launch_initialize()
653 {
654         int rc;
655         gid_t r, e, s;
656
657         getresgid(&r, &e, &s);
658         if (s && s != e)
659                 groupid = s;
660         else
661                 groupid = -1;
662
663         rc = read_configuration_file(FWK_LAUNCH_CONF);
664         dump_launchers();
665         return rc;
666 }
667
668 static struct desc_list *search_launcher(const char *type, enum afm_launch_mode mode)
669 {
670         struct desc_list *dl;
671         struct type_list *tl;
672
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))
677                                         return dl;
678         return NULL;
679 }
680
681 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
682 {
683         char datadir[PATH_MAX];
684         int rc;
685         char secret[9];
686         struct launchparam params;
687         const char *type;
688         struct desc_list *dl;
689
690         /* should be init */
691         assert(groupid != 0);
692         assert(launch_mode_is_valid(desc->mode));
693
694         /* init */
695         children[0] = 0;
696         children[1] = 0;
697
698         /* what launcher ? */
699         type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
700         dl = search_launcher(type, desc->mode);
701         if (dl == NULL) {
702                 ERROR("type %s not found for mode %s!", type, name_of_launch_mode(desc->mode));
703                 errno = ENOENT;
704                 return -1;
705         }
706
707         /* prepare paths */
708         rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
709         if (rc < 0 || rc >= sizeof datadir) {
710                 ERROR("overflow for datadir");
711                 errno = EINVAL;
712                 return -1;
713         }
714
715         /* make the secret and port */
716         mksecret(secret);
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];
722
723         return params.slave ? launchexec2(desc, children, &params) : launchexec1(desc, children, &params);
724 }
725