46bc4e02e0489c58cea126921a3fe7a319984674
[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         char **uri;
56         const char *secret;
57         const char *datadir;
58         const char **master;
59         const char **slave;
60 };
61
62 struct confread {
63         const char *filepath;
64         FILE *file;
65         int lineno;
66         int index;
67         int length;
68         char buffer[4096];
69 };
70
71 struct desc_list *launchers = NULL;
72
73 static gid_t groupid = 0;
74
75 const char separators[] = " \t\n";
76
77 static void dump_launchers()
78 {
79         int j, k;
80         struct desc_list *desc;
81         struct type_list *type;
82
83         for (desc = launchers ; desc != NULL ; desc = desc->next) {
84                 printf("mode %s\n", name_of_launch_mode(desc->mode));
85                 for (type = desc->types ; type != NULL ; type = type->next)
86                         printf("%s\n", type->type);
87                 for ( j = 0 ; j < 2 ; j++)
88                         if (desc->execs[j] != NULL) {
89                                 for (k = 0 ; desc->execs[j][k] != NULL ; k++)
90                                         printf("  %s", desc->execs[j][k]);
91                                 printf("\n");
92                         }
93                 printf("\n");
94         }
95 }
96
97 static int next_token(struct confread *cread)
98 {
99         int idx = cread->index + cread->length;
100         cread->index = idx + strspn(&cread->buffer[idx], separators);
101         cread->length = strcspn(&cread->buffer[cread->index], separators);
102         return cread->length;
103 }
104
105 static int read_line(struct confread *cread)
106 {
107         while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
108                 cread->lineno++;
109                 cread->index = strspn(cread->buffer, separators);
110                 if (cread->buffer[cread->index] && cread->buffer[cread->index] != '#') {
111                         cread->length = strcspn(&cread->buffer[cread->index], separators);
112                         assert(cread->length > 0);
113                         return cread->length;
114                 }
115         }
116         if (ferror(cread->file)) {
117                 ERROR("%s:%d: error while reading, %m", cread->filepath, cread->lineno);
118                 return -1;
119         }
120         return 0;
121 }
122
123 static char **read_vector(struct confread *cread)
124 {
125         int index0, length0;
126         char **vector, *args;
127         int count, length;
128
129         /* record origin */
130         index0 = cread->index;
131         length0 = cread->length;
132
133         /* count */
134         count = 0;
135         length = 0;
136         while(cread->length) {
137                 count++;
138                 length += cread->length;
139                 next_token(cread);
140         }
141
142         /* allocates */
143         cread->index = index0;
144         cread->length = length0;
145         vector = malloc(length + count + (count + 1) * sizeof(char*));
146         if (vector == NULL)
147                 return NULL;
148
149         /* copies */
150         args = (char*)(vector + count + 1);
151         count = 0;
152         while(cread->length) {
153                 vector[count++] = args;
154                 memcpy(args, &cread->buffer[cread->index], cread->length);
155                 args += cread->length;
156                 *args++ = 0;
157                 next_token(cread);
158         }
159         vector[count] = NULL;
160         cread->index = index0;
161         cread->length = length0;
162         return vector;
163 }
164
165 static struct type_list *read_type(struct confread *cread)
166 {
167         int index, length;
168         struct type_list *result;
169
170         /* record index and length */
171         index = cread->index;
172         length = cread->length;
173
174         /* check no extra characters */
175         if (next_token(cread)) {
176                 ERROR("%s:%d: extra characters found after type %.*s",
177                         cread->filepath, cread->lineno, length, &cread->buffer[index]);
178                 errno = EINVAL;
179                 return NULL;
180         }
181
182         /* allocate structure */
183         result = malloc(sizeof(struct type_list) + length);
184         if (result == NULL) {
185                 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
186                 errno = ENOMEM;
187                 return NULL;
188         }
189
190         /* fill the structure */
191         memcpy(result->type, &cread->buffer[index], length);
192         result->type[length] = 0;
193         return result;
194 }
195
196 static enum afm_launch_mode read_mode(struct confread *cread)
197 {
198         int index, length;
199         enum afm_launch_mode result;
200
201         assert(cread->index == 0);
202         assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
203
204         /* get the next token: the mode string */
205         if (!next_token(cread)) {
206                 ERROR("%s:%d: no mode value set", cread->filepath, cread->lineno);
207                 errno = EINVAL;
208                 return invalid_launch_mode;
209         }
210
211         /* record index and length */
212         index = cread->index;
213         length = cread->length;
214
215         /* check no extra characters */
216         if (next_token(cread)) {
217                 ERROR("%s:%d: extra characters found after mode %.*s",
218                         cread->filepath, cread->lineno, length, &cread->buffer[index]);
219                 errno = EINVAL;
220                 return invalid_launch_mode;
221         }
222
223         /* get the mode */
224         cread->buffer[index + length] = 0;
225         result = launch_mode_of_string(&cread->buffer[index]);
226         if (result == invalid_launch_mode) {
227                 ERROR("%s:%d: invalid mode value %s",
228                         cread->filepath, cread->lineno, &cread->buffer[index]);
229                 errno = EINVAL;
230         }
231         return result;
232 }
233
234 static void free_type_list(struct type_list *types)
235 {
236         while (types != NULL) {
237                 struct type_list *next = types->next;
238                 free(types);
239                 types = next;
240         }
241 }
242
243 static int read_launchers(struct confread *cread)
244 {
245         int rc;
246         struct type_list *types, *lt;
247         struct desc_list *desc;
248         enum afm_launch_mode mode;
249         char **vector;
250
251         /* reads the file */
252         lt = NULL;
253         types = NULL;
254         desc = NULL;
255         mode = invalid_launch_mode;
256         rc = read_line(cread);
257         while (rc > 0) {
258                 if (cread->index == 0) {
259                         if (cread->length == 4
260                         && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
261                                 /* check if allowed */
262                                 if (types != NULL) {
263                                         ERROR("%s:%d: mode found before launch vector",
264                                                 cread->filepath, cread->lineno);
265                                         errno = EINVAL;
266                                         free_type_list(types);
267                                         return -1;
268                                 }
269
270                                 /* read the mode */
271                                 mode = read_mode(cread);
272                                 if (mode == invalid_launch_mode)
273                                         return -1;
274                         } else {
275                                 if (mode == invalid_launch_mode) {
276                                         ERROR("%s:%d: mode not found before type",
277                                                         cread->filepath, cread->lineno);
278                                         errno = EINVAL;
279                                         assert(types == NULL);
280                                         return -1;
281                                 }
282                                 /* read a type */
283                                 lt = read_type(cread);
284                                 if (lt == NULL) {
285                                         free_type_list(types);
286                                         return -1;
287                                 }
288                                 lt->next = types;
289                                 types = lt;
290                         }
291                         desc = NULL;
292                 } else if (types == NULL && desc == NULL) {
293                         if (lt == NULL)
294                                 ERROR("%s:%d: untyped launch vector found",
295                                         cread->filepath, cread->lineno);
296                         else
297                                 ERROR("%s:%d: extra launch vector found (2 max)",
298                                         cread->filepath, cread->lineno);
299                         errno = EINVAL;
300                         return -1;
301                 } else {
302                         vector = read_vector(cread);
303                         if (vector == NULL) {
304                                 ERROR("%s:%d: out of memory",
305                                         cread->filepath, cread->lineno);
306                                 free_type_list(types);
307                                 errno = ENOMEM;
308                                 return -1;
309                         }
310                         if (types) {
311                                 assert(desc == NULL);
312                                 desc = malloc(sizeof * desc);
313                                 if (desc == NULL) {
314                                         ERROR("%s:%d: out of memory",
315                                                 cread->filepath, cread->lineno);
316                                         free_type_list(types);
317                                         errno = ENOMEM;
318                                         return -1;
319                                 }
320                                 desc->next = launchers;
321                                 desc->mode = mode;
322                                 desc->types = types;
323                                 desc->execs[0] = vector;
324                                 desc->execs[1] = NULL;
325                                 types = NULL;
326                                 launchers = desc;
327                         } else {
328                                 desc->execs[1] = vector;
329                                 desc = NULL;
330                         }
331                 }
332                 rc = read_line(cread);
333         }
334         if (types != NULL) {
335                 ERROR("%s:%d: end of file found before launch vector",
336                         cread->filepath, cread->lineno);
337                 free_type_list(types);
338                 errno = EINVAL;
339                 return -1;
340         }
341         return rc;
342 }
343
344 static int read_configuration_file(const char *filepath)
345 {
346         int rc;
347         struct confread cread;
348
349         /* opens the configuration file */
350         cread.file = fopen(filepath, "r");
351         if (cread.file == NULL) {
352                 /* error */
353                 ERROR("can't read file %s: %m", filepath);
354                 rc = -1;
355         } else {
356                 /* reads it */
357                 cread.filepath = filepath;
358                 cread.lineno = 0;
359                 rc = read_launchers(&cread);
360                 fclose(cread.file);
361         }
362         return rc;
363 }
364
365 /*
366 %I icondir                      FWK_ICON_DIR
367 %P port                         params->port
368 %S secret                       params->secret
369 %D datadir                      params->datadir
370 %r rootdir                      desc->path
371 %h homedir                      desc->home
372 %t tag (smack label)            desc->tag
373 %a appid                        desc->appid
374 %c content                      desc->content
375 %m mime-type                    desc->type
376 %n name                         desc->name
377 %p plugins                      desc->plugins
378 %W width                        desc->width
379 %H height                       desc->height
380 %% %
381 */
382
383 union arguments {
384         char *scalar;
385         char **vector;
386 };
387
388 static union arguments instantiate_arguments(
389         const char            **args,
390         struct afm_launch_desc *desc,
391         struct launchparam     *params,
392         int                     wants_vector
393 )
394 {
395         const char **iter, *p, *v;
396         char *data, port[20], width[20], height[20], mini[3], c, sep;
397         int n, s;
398         union arguments result;
399
400         /* init */
401         sep = wants_vector ? 0 : ' ';
402         mini[0] = '%';
403         mini[2] = 0;
404
405         /* loop that either compute the size and build the result */
406         data = NULL;
407         n = s = 0;
408         for (;;) {
409                 iter = args;
410                 n = 0;
411                 while (*iter) {
412                         p = *iter++;
413                         if (data && !sep)
414                                 result.vector[n] = data;
415                         n++;
416                         while((c = *p++) != 0) {
417                                 if (c != '%') {
418                                         if (data)
419                                                 *data++ = c;
420                                         else
421                                                 s++;
422                                 } else {
423                                         c = *p++;
424                                         switch (c) {
425                                         case 'I': v = FWK_ICON_DIR; break;
426                                         case 'S': v = params->secret; break;
427                                         case 'D': v = params->datadir; break;
428                                         case 'r': v = desc->path; break;
429                                         case 'h': v = desc->home; break;
430                                         case 't': v = desc->tag; break;
431                                         case 'a': v = desc->appid; break;
432                                         case 'c': v = desc->content; break;
433                                         case 'm': v = desc->type; break;
434                                         case 'n': v = desc->name; break;
435                                         case 'p': v = "" /*desc->plugins*/; break;
436                                         case 'P':
437                                                 if(!data)
438                                                         sprintf(port, "%d", params->port);
439                                                 v = port;
440                                                 break;
441                                         case 'W':
442                                                 if(!data)
443                                                         sprintf(width, "%d", desc->width);
444                                                 v = width;
445                                                 break;
446                                         case 'H':
447                                                 if(!data)
448                                                         sprintf(height, "%d", desc->height);
449                                                 v = height;
450                                                 break;
451                                         case '%':
452                                                 c = 0;
453                                         default:
454                                                 mini[1] = c;
455                                                 v = mini;
456                                                 break;
457                                         }
458                                         if (data)
459                                                 data = stpcpy(data, v);
460                                         else
461                                                 s += strlen(v);
462                                 }
463                         }
464                         if (data)
465                                 *data++ = sep;
466                         else
467                                 s++;
468                 }
469                 if (sep) {
470                         assert(!wants_vector);
471                         if (data) {
472                                 *--data = 0;
473                                 return result;
474                         }
475                         /* allocation */
476                         result.scalar = malloc(s);
477                         if (result.scalar == NULL) {
478                                 errno = ENOMEM;
479                                 return result;
480                         }
481                         data = result.scalar;
482                 } else {
483                         assert(wants_vector);
484                         if (data) {
485                                 result.vector[n] = NULL;
486                                 return result;
487                         }
488                         /* allocation */
489                         result.vector = malloc((n+1)*sizeof(char*) + s);
490                         if (result.vector == NULL) {
491                                 errno = ENOMEM;
492                                 return result;
493                         }
494                         data = (char*)(&result.vector[n + 1]);
495                 }
496         }
497 }
498
499 static void mksecret(char buffer[9])
500 {
501         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
502 }
503
504 static int mkport()
505 {
506         static int port_ring = 12345;
507         int port = port_ring;
508         if (port < 12345 || port > 15432)
509                 port = 12345;
510         port_ring = port + 1;
511         return port;
512 }
513
514 static int launch_local_1(
515         struct afm_launch_desc *desc,
516         pid_t                   children[2],
517         struct launchparam     *params
518 )
519 {
520         int rc;
521         char **args;
522
523         /* fork the master child */
524         children[0] = fork();
525         if (children[0] < 0) {
526                 ERROR("master fork failed: %m");
527                 return -1;
528         }
529         if (children[0]) {
530                 /********* in the parent process ************/
531                 return 0;
532         }
533
534         /********* in the master child ************/
535
536         /* avoid set-gid effect */
537         setresgid(groupid, groupid, groupid);
538
539         /* enter the process group */
540         rc = setpgid(0, 0);
541         if (rc) {
542                 ERROR("setpgid failed");
543                 _exit(1);
544         }
545
546         /* enter security mode */
547         rc = secmgr_prepare_exec(desc->tag);
548         if (rc < 0) {
549                 ERROR("call to secmgr_prepare_exec failed: %m");
550                 _exit(1);
551         }
552
553         /* enter the datadirectory */
554         rc = mkdir(params->datadir, 0755);
555         if (rc && errno != EEXIST) {
556                 ERROR("creation of datadir %s failed: %m", params->datadir);
557                 _exit(1);
558         }
559         rc = chdir(params->datadir);
560         if (rc) {
561                 ERROR("can't enter the datadir %s: %m", params->datadir);
562                 _exit(1);
563         }
564
565         args = instantiate_arguments(params->master, desc, params, 1).vector;
566         if (args == NULL) {
567                 ERROR("out of memory in master");
568         }
569         else {
570                 rc = execve(args[0], args, environ);
571                 ERROR("failed to exec master %s: %m", args[0]);
572         }
573         _exit(1);
574 }
575
576 static int launch_local_2(
577         struct afm_launch_desc *desc,
578         pid_t                   children[2],
579         struct launchparam     *params
580 )
581 {
582         int rc;
583         char message[10];
584         int mpipe[2];
585         int spipe[2];
586         char **args;
587
588         /* prepare the pipes */
589         rc = pipe2(mpipe, O_CLOEXEC);
590         if (rc < 0) {
591                 ERROR("error while calling pipe2: %m");
592                 return -1;
593         }
594         rc = pipe2(spipe, O_CLOEXEC);
595         if (rc < 0) {
596                 ERROR("error while calling pipe2: %m");
597                 close(spipe[0]);
598                 close(spipe[1]);
599                 return -1;
600         }
601
602         /* fork the master child */
603         children[0] = fork();
604         if (children[0] < 0) {
605                 ERROR("master fork failed: %m");
606                 close(mpipe[0]);
607                 close(mpipe[1]);
608                 close(spipe[0]);
609                 close(spipe[1]);
610                 return -1;
611         }
612         if (children[0]) {
613                 /********* in the parent process ************/
614                 close(mpipe[1]);
615                 close(spipe[0]);
616                 /* wait the ready signal (that transmit the slave pid) */
617                 rc = read(mpipe[0], &children[1], sizeof children[1]);
618                 close(mpipe[0]);
619                 if (rc  <= 0) {
620                         ERROR("reading master pipe failed: %m");
621                         close(spipe[1]);
622                         return -1;
623                 }
624                 assert(rc == sizeof children[1]);
625                 /* start the child */
626                 rc = write(spipe[1], "start", 5);
627                 if (rc < 0) {
628                         ERROR("writing slave pipe failed: %m");
629                         close(spipe[1]);
630                         return -1;
631                 }
632                 assert(rc == 5);
633                 close(spipe[1]);
634                 return 0;
635         }
636
637         /********* in the master child ************/
638         close(mpipe[0]);
639         close(spipe[1]);
640
641         /* avoid set-gid effect */
642         setresgid(groupid, groupid, groupid);
643
644         /* enter the process group */
645         rc = setpgid(0, 0);
646         if (rc) {
647                 ERROR("setpgid failed");
648                 _exit(1);
649         }
650
651         /* enter security mode */
652         rc = secmgr_prepare_exec(desc->tag);
653         if (rc < 0) {
654                 ERROR("call to secmgr_prepare_exec failed: %m");
655                 _exit(1);
656         }
657
658         /* enter the datadirectory */
659         rc = mkdir(params->datadir, 0755);
660         if (rc && errno != EEXIST) {
661                 ERROR("creation of datadir %s failed: %m", params->datadir);
662                 _exit(1);
663         }
664         rc = chdir(params->datadir);
665         if (rc) {
666                 ERROR("can't enter the datadir %s: %m", params->datadir);
667                 _exit(1);
668         }
669
670         /* fork the slave child */
671         children[1] = fork();
672         if (children[1] < 0) {
673                 ERROR("slave fork failed: %m");
674                 _exit(1);
675         }
676         if (children[1] == 0) {
677                 /********* in the slave child ************/
678                 close(mpipe[0]);
679                 rc = read(spipe[0], message, sizeof message);
680                 if (rc <= 0) {
681                         ERROR("reading slave pipe failed: %m");
682                         _exit(1);
683                 }
684
685                 args = instantiate_arguments(params->slave, desc, params, 1).vector;
686                 if (args == NULL) {
687                         ERROR("out of memory in slave");
688                 }
689                 else {
690                         rc = execve(args[0], args, environ);
691                         ERROR("failed to exec slave %s: %m", args[0]);
692                 }
693                 _exit(1);
694         }
695
696         /********* still in the master child ************/
697         close(spipe[1]);
698         args = instantiate_arguments(params->master, desc, params, 1).vector;
699         if (args == NULL) {
700                 ERROR("out of memory in master");
701         }
702         else {
703                 rc = write(mpipe[1], &children[1], sizeof children[1]);
704                 if (rc <= 0) {
705                         ERROR("can't write master pipe: %m");
706                 }
707                 else {
708                         close(mpipe[1]);
709                         rc = execve(args[0], args, environ);
710                         ERROR("failed to exec master %s: %m", args[0]);
711                 }
712         }
713         _exit(1);
714 }
715
716 static int launch_local(
717         struct afm_launch_desc *desc,
718         pid_t                   children[2],
719         struct launchparam     *params
720 )
721 {
722         if (params->slave == NULL)
723                 return launch_local_1(desc, children, params);
724         return launch_local_2(desc, children, params);
725 }
726
727 static int launch_remote(
728         struct afm_launch_desc *desc,
729         pid_t                   children[2],
730         struct launchparam     *params
731 )
732 {
733         int rc;
734         char *uri;
735
736         /* instanciate the uri */
737         if (params->slave == NULL)
738                 uri = strdup("");
739         else
740                 uri = instantiate_arguments(params->slave, desc, params, 0).scalar;
741         if (uri == NULL) {
742                 ERROR("out of memory for remote uri");
743                 errno = ENOMEM;
744                 return -1;
745         }
746
747         /* launch the command */
748         rc = launch_local_1(desc, children, params);
749         if (rc)
750                 free(uri);
751         else
752                 *params->uri = uri;
753         return rc;
754 }
755
756 int afm_launch_initialize()
757 {
758         int rc;
759         gid_t r, e, s;
760
761         getresgid(&r, &e, &s);
762         if (s && s != e)
763                 groupid = s;
764         else
765                 groupid = -1;
766
767         rc = read_configuration_file(FWK_LAUNCH_CONF);
768         dump_launchers();
769         return rc;
770 }
771
772 static struct desc_list *search_launcher(const char *type, enum afm_launch_mode mode)
773 {
774         struct desc_list *dl;
775         struct type_list *tl;
776
777         for (dl = launchers ; dl ; dl = dl->next)
778                 if (dl->mode == mode)
779                         for (tl = dl->types ; tl != NULL ; tl = tl->next)
780                                 if (!strcmp(tl->type, type))
781                                         return dl;
782         return NULL;
783 }
784
785 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
786 {
787         int rc;
788         char datadir[PATH_MAX];
789         char secret[9];
790         struct launchparam params;
791         const char *type;
792         struct desc_list *dl;
793
794         /* should be init */
795         assert(groupid != 0);
796         assert(launch_mode_is_valid(desc->mode));
797         assert(desc->mode == mode_local || uri != NULL);
798         assert(uri == NULL || *uri == NULL);
799
800         /* init */
801         children[0] = 0;
802         children[1] = 0;
803
804         /* what launcher ? */
805         type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
806         dl = search_launcher(type, desc->mode);
807         if (dl == NULL) {
808                 ERROR("type %s not found for mode %s!", type, name_of_launch_mode(desc->mode));
809                 errno = ENOENT;
810                 return -1;
811         }
812
813         /* prepare paths */
814         rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
815         if (rc < 0 || rc >= sizeof datadir) {
816                 ERROR("overflow for datadir");
817                 errno = EINVAL;
818                 return -1;
819         }
820
821         /* make the secret and port */
822         mksecret(secret);
823         params.uri = uri;
824         params.port = mkport();
825         params.secret = secret;
826         params.datadir = datadir;
827         params.master = (const char **)dl->execs[0];
828         params.slave = (const char **)dl->execs[1];
829
830         switch (desc->mode) {
831         case mode_local:
832                 return launch_local(desc, children, &params);
833         case mode_remote:
834                 return launch_remote(desc, children, &params);
835         default:
836                 assert(0);
837                 return -1;
838         }
839 }
840