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