afm-launch: moves initialisation at bottom
[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         result.vector = NULL;
407         result.scalar = NULL;
408         data = NULL;
409         n = s = 0;
410         for (;;) {
411                 iter = args;
412                 n = 0;
413                 while (*iter) {
414                         p = *iter++;
415                         if (data && !sep)
416                                 result.vector[n] = data;
417                         n++;
418                         while((c = *p++) != 0) {
419                                 if (c != '%') {
420                                         if (data)
421                                                 *data++ = c;
422                                         else
423                                                 s++;
424                                 } else {
425                                         c = *p++;
426                                         switch (c) {
427                                         case 'I': v = FWK_ICON_DIR; break;
428                                         case 'S': v = params->secret; break;
429                                         case 'D': v = params->datadir; break;
430                                         case 'r': v = desc->path; break;
431                                         case 'h': v = desc->home; break;
432                                         case 't': v = desc->tag; break;
433                                         case 'a': v = desc->appid; break;
434                                         case 'c': v = desc->content; break;
435                                         case 'm': v = desc->type; break;
436                                         case 'n': v = desc->name; break;
437                                         case 'p': v = "" /*desc->plugins*/; break;
438                                         case 'P':
439                                                 if(!data)
440                                                         sprintf(port, "%d", params->port);
441                                                 v = port;
442                                                 break;
443                                         case 'W':
444                                                 if(!data)
445                                                         sprintf(width, "%d", desc->width);
446                                                 v = width;
447                                                 break;
448                                         case 'H':
449                                                 if(!data)
450                                                         sprintf(height, "%d", desc->height);
451                                                 v = height;
452                                                 break;
453                                         case '%':
454                                                 c = 0;
455                                         default:
456                                                 mini[1] = c;
457                                                 v = mini;
458                                                 break;
459                                         }
460                                         if (data)
461                                                 data = stpcpy(data, v);
462                                         else
463                                                 s += strlen(v);
464                                 }
465                         }
466                         if (data)
467                                 *data++ = sep;
468                         else
469                                 s++;
470                 }
471                 if (sep) {
472                         assert(!wants_vector);
473                         if (data) {
474                                 *--data = 0;
475                                 return result;
476                         }
477                         /* allocation */
478                         result.scalar = malloc(s);
479                         if (result.scalar == NULL) {
480                                 errno = ENOMEM;
481                                 return result;
482                         }
483                         data = result.scalar;
484                 } else {
485                         assert(wants_vector);
486                         if (data) {
487                                 result.vector[n] = NULL;
488                                 return result;
489                         }
490                         /* allocation */
491                         result.vector = malloc((n+1)*sizeof(char*) + s);
492                         if (result.vector == NULL) {
493                                 errno = ENOMEM;
494                                 return result;
495                         }
496                         data = (char*)(&result.vector[n + 1]);
497                 }
498         }
499 }
500
501 static void mksecret(char buffer[9])
502 {
503         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
504 }
505
506 static int mkport()
507 {
508         static int port_ring = 12345;
509         int port = port_ring;
510         if (port < 12345 || port > 15432)
511                 port = 12345;
512         port_ring = port + 1;
513         return port;
514 }
515
516 static int launch_local_1(
517         struct afm_launch_desc *desc,
518         pid_t                   children[2],
519         struct launchparam     *params
520 )
521 {
522         int rc;
523         char **args;
524
525         /* fork the master child */
526         children[0] = fork();
527         if (children[0] < 0) {
528                 ERROR("master fork failed: %m");
529                 return -1;
530         }
531         if (children[0]) {
532                 /********* in the parent process ************/
533                 return 0;
534         }
535
536         /********* in the master child ************/
537
538         /* avoid set-gid effect */
539         setresgid(groupid, groupid, groupid);
540
541         /* enter the process group */
542         rc = setpgid(0, 0);
543         if (rc) {
544                 ERROR("setpgid failed");
545                 _exit(1);
546         }
547
548         /* enter security mode */
549         rc = secmgr_prepare_exec(desc->tag);
550         if (rc < 0) {
551                 ERROR("call to secmgr_prepare_exec failed: %m");
552                 _exit(1);
553         }
554
555         /* enter the datadirectory */
556         rc = mkdir(params->datadir, 0755);
557         if (rc && errno != EEXIST) {
558                 ERROR("creation of datadir %s failed: %m", params->datadir);
559                 _exit(1);
560         }
561         rc = chdir(params->datadir);
562         if (rc) {
563                 ERROR("can't enter the datadir %s: %m", params->datadir);
564                 _exit(1);
565         }
566
567         args = instantiate_arguments(params->master, desc, params, 1).vector;
568         if (args == NULL) {
569                 ERROR("out of memory in master");
570         }
571         else {
572                 rc = execve(args[0], args, environ);
573                 ERROR("failed to exec master %s: %m", args[0]);
574         }
575         _exit(1);
576 }
577
578 static int launch_local_2(
579         struct afm_launch_desc *desc,
580         pid_t                   children[2],
581         struct launchparam     *params
582 )
583 {
584         int rc;
585         char message[10];
586         int mpipe[2];
587         int spipe[2];
588         char **args;
589
590         /* prepare the pipes */
591         rc = pipe2(mpipe, O_CLOEXEC);
592         if (rc < 0) {
593                 ERROR("error while calling pipe2: %m");
594                 return -1;
595         }
596         rc = pipe2(spipe, O_CLOEXEC);
597         if (rc < 0) {
598                 ERROR("error while calling pipe2: %m");
599                 close(spipe[0]);
600                 close(spipe[1]);
601                 return -1;
602         }
603
604         /* fork the master child */
605         children[0] = fork();
606         if (children[0] < 0) {
607                 ERROR("master fork failed: %m");
608                 close(mpipe[0]);
609                 close(mpipe[1]);
610                 close(spipe[0]);
611                 close(spipe[1]);
612                 return -1;
613         }
614         if (children[0]) {
615                 /********* in the parent process ************/
616                 close(mpipe[1]);
617                 close(spipe[0]);
618                 /* wait the ready signal (that transmit the slave pid) */
619                 rc = read(mpipe[0], &children[1], sizeof children[1]);
620                 close(mpipe[0]);
621                 if (rc  <= 0) {
622                         ERROR("reading master pipe failed: %m");
623                         close(spipe[1]);
624                         return -1;
625                 }
626                 assert(rc == sizeof children[1]);
627                 /* start the child */
628                 rc = write(spipe[1], "start", 5);
629                 if (rc < 0) {
630                         ERROR("writing slave pipe failed: %m");
631                         close(spipe[1]);
632                         return -1;
633                 }
634                 assert(rc == 5);
635                 close(spipe[1]);
636                 return 0;
637         }
638
639         /********* in the master child ************/
640         close(mpipe[0]);
641         close(spipe[1]);
642
643         /* avoid set-gid effect */
644         setresgid(groupid, groupid, groupid);
645
646         /* enter the process group */
647         rc = setpgid(0, 0);
648         if (rc) {
649                 ERROR("setpgid failed");
650                 _exit(1);
651         }
652
653         /* enter security mode */
654         rc = secmgr_prepare_exec(desc->tag);
655         if (rc < 0) {
656                 ERROR("call to secmgr_prepare_exec failed: %m");
657                 _exit(1);
658         }
659
660         /* enter the datadirectory */
661         rc = mkdir(params->datadir, 0755);
662         if (rc && errno != EEXIST) {
663                 ERROR("creation of datadir %s failed: %m", params->datadir);
664                 _exit(1);
665         }
666         rc = chdir(params->datadir);
667         if (rc) {
668                 ERROR("can't enter the datadir %s: %m", params->datadir);
669                 _exit(1);
670         }
671
672         /* fork the slave child */
673         children[1] = fork();
674         if (children[1] < 0) {
675                 ERROR("slave fork failed: %m");
676                 _exit(1);
677         }
678         if (children[1] == 0) {
679                 /********* in the slave child ************/
680                 close(mpipe[0]);
681                 rc = read(spipe[0], message, sizeof message);
682                 if (rc <= 0) {
683                         ERROR("reading slave pipe failed: %m");
684                         _exit(1);
685                 }
686
687                 args = instantiate_arguments(params->slave, desc, params, 1).vector;
688                 if (args == NULL) {
689                         ERROR("out of memory in slave");
690                 }
691                 else {
692                         rc = execve(args[0], args, environ);
693                         ERROR("failed to exec slave %s: %m", args[0]);
694                 }
695                 _exit(1);
696         }
697
698         /********* still in the master child ************/
699         close(spipe[1]);
700         args = instantiate_arguments(params->master, desc, params, 1).vector;
701         if (args == NULL) {
702                 ERROR("out of memory in master");
703         }
704         else {
705                 rc = write(mpipe[1], &children[1], sizeof children[1]);
706                 if (rc <= 0) {
707                         ERROR("can't write master pipe: %m");
708                 }
709                 else {
710                         close(mpipe[1]);
711                         rc = execve(args[0], args, environ);
712                         ERROR("failed to exec master %s: %m", args[0]);
713                 }
714         }
715         _exit(1);
716 }
717
718 static int launch_local(
719         struct afm_launch_desc *desc,
720         pid_t                   children[2],
721         struct launchparam     *params
722 )
723 {
724         if (params->slave == NULL)
725                 return launch_local_1(desc, children, params);
726         return launch_local_2(desc, children, params);
727 }
728
729 static int launch_remote(
730         struct afm_launch_desc *desc,
731         pid_t                   children[2],
732         struct launchparam     *params
733 )
734 {
735         int rc;
736         char *uri;
737
738         /* instanciate the uri */
739         if (params->slave == NULL)
740                 uri = NULL;
741         else
742                 uri = instantiate_arguments(params->slave, desc, params, 0).scalar;
743         if (uri == NULL) {
744                 ERROR("out of memory for remote uri");
745                 errno = ENOMEM;
746                 return -1;
747         }
748
749         /* launch the command */
750         rc = launch_local_1(desc, children, params);
751         if (rc)
752                 free(uri);
753         else
754                 *params->uri = uri;
755         return rc;
756 }
757
758 static struct desc_list *search_launcher(const char *type, enum afm_launch_mode mode)
759 {
760         struct desc_list *dl;
761         struct type_list *tl;
762
763         for (dl = launchers ; dl ; dl = dl->next)
764                 if (dl->mode == mode)
765                         for (tl = dl->types ; tl != NULL ; tl = tl->next)
766                                 if (!strcmp(tl->type, type))
767                                         return dl;
768         return NULL;
769 }
770
771 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
772 {
773         int rc;
774         char datadir[PATH_MAX];
775         char secret[9];
776         struct launchparam params;
777         const char *type;
778         struct desc_list *dl;
779
780         /* should be init */
781         assert(groupid != 0);
782         assert(launch_mode_is_valid(desc->mode));
783         assert(desc->mode == mode_local || uri != NULL);
784         assert(uri == NULL || *uri == NULL);
785
786         /* init */
787         children[0] = 0;
788         children[1] = 0;
789
790         /* what launcher ? */
791         type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
792         dl = search_launcher(type, desc->mode);
793         if (dl == NULL) {
794                 ERROR("type %s not found for mode %s!", type, name_of_launch_mode(desc->mode));
795                 errno = ENOENT;
796                 return -1;
797         }
798
799         /* prepare paths */
800         rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
801         if (rc < 0 || rc >= sizeof datadir) {
802                 ERROR("overflow for datadir");
803                 errno = EINVAL;
804                 return -1;
805         }
806
807         /* make the secret and port */
808         mksecret(secret);
809         params.uri = uri;
810         params.port = mkport();
811         params.secret = secret;
812         params.datadir = datadir;
813         params.master = (const char **)dl->execs[0];
814         params.slave = (const char **)dl->execs[1];
815
816         switch (desc->mode) {
817         case mode_local:
818                 return launch_local(desc, children, &params);
819         case mode_remote:
820                 return launch_remote(desc, children, &params);
821         default:
822                 assert(0);
823                 return -1;
824         }
825 }
826
827 int afm_launch_initialize()
828 {
829         int rc;
830         gid_t r, e, s;
831
832         getresgid(&r, &e, &s);
833         if (s && s != e)
834                 groupid = s;
835         else
836                 groupid = -1;
837
838         rc = read_configuration_file(FWK_LAUNCH_CONF);
839         dump_launchers();
840         return rc;
841 }
842