afm-user-daemon: prepare to launch with mode
[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 const char separators[] = " \t\n";
42
43 struct execdesc {
44         char *type;
45         char **execs[2];
46 };
47
48 struct launchers {
49         int count;
50         struct execdesc *descs;
51 };
52
53 static struct launchers launchers = { 0, NULL };
54
55 struct launchparam {
56         int port;
57         const char *secret;
58         const char *datadir;
59         const char **master;
60         const char **slave;
61 };
62
63 static gid_t groupid = 0;
64
65 struct confread {
66         const char *filepath;
67         FILE *file;
68         int lineno;
69         int index;
70         int length;
71         char buffer[4096];
72 };
73
74 static void dump_launchers(struct launchers *launchs)
75 {
76         int i, j, k;
77         for (i = 0 ; i < launchs->count ; i++) {
78                 printf("%s\n", launchs->descs[i].type);
79                 for ( j = 0 ; j < 2 ; j++)
80                         if (launchs->descs[i].execs[j] != NULL) {
81                                 for (k = 0 ; launchs->descs[i].execs[j][k] != NULL ; k++)
82                                         printf("  %s", launchs->descs[i].execs[j][k]);
83                                 printf("\n");
84                         }
85         }
86 }
87
88 static int next_token(struct confread *cread)
89 {
90         cread->index += cread->length + strspn(&cread->buffer[cread->index + cread->length], separators);
91         cread->length = strcspn(&cread->buffer[cread->index], separators);
92         return cread->length;
93 }
94
95 static int read_line(struct confread *cread)
96 {
97         while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
98                 cread->lineno++;
99                 cread->index = strspn(cread->buffer, separators);
100                 if (cread->buffer[cread->index] && cread->buffer[cread->index] != '#') {
101                         cread->length = strcspn(&cread->buffer[cread->index], separators);
102                         assert(cread->length > 0);
103                         return cread->length;
104                 }
105         }
106         if (ferror(cread->file)) {
107                 ERROR("%s:%d: error while reading, %m", cread->filepath, cread->lineno);
108                 return -1;
109         }
110         return 0;
111 }
112
113 static char *dup_token(struct confread *cread)
114 {
115         assert(cread->length);
116         return strndup(&cread->buffer[cread->index], cread->length);
117 }
118
119 static char **dup_tokens_vector(struct confread *cread)
120 {
121         int index0, length0;
122         char **vector, *args;
123         int count, length;
124
125         /* record origin */
126         index0 = cread->index;
127         length0 = cread->length;
128
129         /* count */
130         count = 0;
131         length = 0;
132         while(cread->length) {
133                 count++;
134                 length += cread->length;
135                 next_token(cread);
136         }
137
138         /* allocates */
139         cread->index = index0;
140         cread->length = length0;
141         vector = malloc(length + count + (count + 1) * sizeof(char*));
142         if (vector == NULL)
143                 return NULL;
144
145         /* copies */
146         args = (char*)(vector + count + 1);
147         count = 0;
148         while(cread->length) {
149                 vector[count++] = args;
150                 memcpy(args, &cread->buffer[cread->index], cread->length);
151                 args += cread->length;
152                 *args++ = 0;
153                 next_token(cread);
154         }
155         vector[count] = NULL;
156         cread->index = index0;
157         cread->length = length0;
158         return vector;
159 }
160
161 static int read_type(struct confread *cread)
162 {
163         int count;
164         struct execdesc *descs;
165         char *type;
166
167         /* get the type */
168         type = dup_token(cread);
169         if (type == NULL) {
170                 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
171                 errno = ENOMEM;
172                 return -1;
173         }
174
175         /* check the type */
176         if (next_token(cread)) {
177                 ERROR("%s:%d: extra characters found after type %s", cread->filepath, cread->lineno, type);
178                 free(type);
179                 errno = EINVAL;
180                 return -1;
181         }
182
183         /* allocates data */
184         count = launchers.count + 1;
185         descs = realloc(launchers.descs, count * sizeof(struct execdesc));
186         if (descs == NULL) {
187                 free(type);
188                 errno = ENOMEM;
189                 return -1;
190         }
191
192         /* fill data */
193         launchers.descs = descs;
194         descs += count - 1;
195         descs->type = type;
196         descs->execs[0] = NULL;
197         descs->execs[1] = NULL;
198         launchers.count = count;
199         return 0;
200 }
201
202 static int read_args(struct confread *cread, int bottom, int offset)
203 {
204         char **vector;
205
206         while (bottom < launchers.count) {
207                 vector = dup_tokens_vector(cread);
208                 if (vector == NULL) {
209                         ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
210                         return -1;
211                 }
212                 launchers.descs[bottom++].execs[offset] = vector;
213         }
214         return 0;
215 }
216
217 static int read_launchers(struct confread *cread)
218 {
219         int rc, bottom, offset, typed;
220
221         /* reads the file */
222         offset = 0;
223         typed = 0;
224         bottom = launchers.count;
225         rc = read_line(cread);
226         while (rc > 0) {
227                 if (cread->index == 0) {
228                         if (!typed)
229                                 bottom = launchers.count;
230                         rc = read_type(cread);
231                         if (rc)
232                                 return rc;
233                         if (!typed) {
234                                 typed = 1;
235                                 offset = 0;
236                         }
237                 } else if (!typed && !offset) {
238                         ERROR("%s:%d: untyped launcher found", cread->filepath, cread->lineno);
239                         errno = EINVAL;
240                         return -1;
241                 } else if (offset >= 2) {
242                         ERROR("%s:%d: extra launcher found", cread->filepath, cread->lineno);
243                         errno = EINVAL;
244                         return -1;
245                 } else {
246                         rc = read_args(cread, bottom, offset);
247                         if (rc)
248                                 return rc;
249                         offset++;
250                         typed = 0;
251                 }
252                 rc = read_line(cread);
253         }
254         return rc;
255 }
256
257 static int read_configuration_file(const char *filepath)
258 {
259         int rc;
260         struct confread cread;
261
262         /* opens the configuration file */
263         cread.file = fopen(filepath, "r");
264         if (cread.file == NULL) {
265                 /* error */
266                 ERROR("can't read file %s: %m", filepath);
267                 rc = -1;
268         } else {
269                 /* reads it */
270                 cread.filepath = filepath;
271                 cread.lineno = 0;
272                 rc = read_launchers(&cread);
273                 fclose(cread.file);
274         }
275         return rc;
276 }
277
278 /*
279 %I icondir                      FWK_ICON_DIR
280 %P port                         params->port
281 %S secret                       params->secret
282 %D datadir                      params->datadir
283 %r rootdir                      desc->path
284 %h homedir                      desc->home
285 %t tag (smack label)            desc->tag
286 %a appid                        desc->appid
287 %c content                      desc->content
288 %m mime-type                    desc->type
289 %n name                         desc->name
290 %p plugins                      desc->plugins
291 %W width                        desc->width
292 %H height                       desc->height
293 %% %
294 */
295
296 static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params)
297 {
298         const char **iter, *p, *v;
299         char *data, **result, port[20], width[20], height[20], mini[3], c;
300         int n, s;
301
302         /* init */
303         mini[0] = '%';
304         mini[2] = 0;
305
306         /* loop that either compute the size and build the result */
307         data = NULL;
308         result = NULL;
309         n = s = 0;
310         for (;;) {
311                 iter = args;
312                 n = 0;
313                 while (*iter) {
314                         p = *iter++;
315                         if (data)
316                                 result[n] = data;
317                         n++;
318                         while((c = *p++) != 0) {
319                                 if (c != '%') {
320                                         if (data)
321                                                 *data++ = c;
322                                         else
323                                                 s++;
324                                 } else {
325                                         c = *p++;
326                                         switch (c) {
327                                         case 'I': v = FWK_ICON_DIR; break;
328                                         case 'P': if(!data) sprintf(port, "%d", params->port); v = port; break;
329                                         case 'S': v = params->secret; break;
330                                         case 'D': v = params->datadir; break;
331                                         case 'r': v = desc->path; break;
332                                         case 'h': v = desc->home; break;
333                                         case 't': v = desc->tag; break;
334                                         case 'a': v = desc->appid; break;
335                                         case 'c': v = desc->content; break;
336                                         case 'm': v = desc->type; break;
337                                         case 'n': v = desc->name; break;
338                                         case 'p': v = "" /*desc->plugins*/; break;
339                                         case 'W': if(!data) sprintf(width, "%d", desc->width); v = width; break;
340                                         case 'H': if(!data) sprintf(height, "%d", desc->height); v = height; break;
341                                         case '%': c = 0;
342                                         default: mini[1] = c; v = mini; break;
343                                         }
344                                         if (data)
345                                                 data = stpcpy(data, v);
346                                         else
347                                                 s += strlen(v);
348                                 }
349                         }
350                         if (data)
351                                 *data++ = 0;
352                         else
353                                 s++;
354                 }
355                 if (data) {
356                         result[n] = NULL;
357                         return result;
358                 }
359                 /* allocation */
360                 result = malloc((n+1)*sizeof(char*) + s);
361                 if (result == NULL) {
362                         errno = ENOMEM;
363                         return NULL;
364                 }
365                 data = (char*)(&result[n + 1]);
366         }
367 }
368
369 static void mksecret(char buffer[9])
370 {
371         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
372 }
373
374 static int mkport()
375 {
376         static int port_ring = 12345;
377         int port = port_ring;
378         if (port < 12345 || port > 15432)
379                 port = 12345;
380         port_ring = port + 1;
381         return port;
382 }
383
384 static int launchexec1(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
385 {
386         int rc;
387         char **args;
388
389         /* fork the master child */
390         children[0] = fork();
391         if (children[0] < 0) {
392                 ERROR("master fork failed: %m");
393                 return -1;
394         }
395         if (children[0]) {
396                 /********* in the parent process ************/
397                 return 0;
398         }
399
400         /********* in the master child ************/
401
402         /* avoid set-gid effect */
403         setresgid(groupid, groupid, groupid);
404
405         /* enter the process group */
406         rc = setpgid(0, 0);
407         if (rc) {
408                 ERROR("setpgid failed");
409                 _exit(1);
410         }
411
412         /* enter security mode */
413         rc = secmgr_prepare_exec(desc->tag);
414         if (rc < 0) {
415                 ERROR("call to secmgr_prepare_exec failed: %m");
416                 _exit(1);
417         }
418
419         /* enter the datadirectory */
420         rc = mkdir(params->datadir, 0755);
421         if (rc && errno != EEXIST) {
422                 ERROR("creation of datadir %s failed: %m", params->datadir);
423                 _exit(1);
424         }
425         rc = chdir(params->datadir);
426         if (rc) {
427                 ERROR("can't enter the datadir %s: %m", params->datadir);
428                 _exit(1);
429         }
430
431         args = instantiate_arguments(params->master, desc, params);
432         if (args == NULL) {
433                 ERROR("out of memory in master");
434         }
435         else {
436                 rc = execve(args[0], args, environ);
437                 ERROR("failed to exec master %s: %m", args[0]);
438         }
439         _exit(1);
440 }
441
442 static int launchexec2(struct afm_launch_desc *desc, pid_t children[2], struct launchparam *params)
443 {
444         int rc;
445         char message[10];
446         int mpipe[2];
447         int spipe[2];
448         char **args;
449
450         /* prepare the pipes */
451         rc = pipe2(mpipe, O_CLOEXEC);
452         if (rc < 0) {
453                 ERROR("error while calling pipe2: %m");
454                 return -1;
455         }
456         rc = pipe2(spipe, O_CLOEXEC);
457         if (rc < 0) {
458                 ERROR("error while calling pipe2: %m");
459                 close(spipe[0]);
460                 close(spipe[1]);
461                 return -1;
462         }
463
464         /* fork the master child */
465         children[0] = fork();
466         if (children[0] < 0) {
467                 ERROR("master fork failed: %m");
468                 close(mpipe[0]);
469                 close(mpipe[1]);
470                 close(spipe[0]);
471                 close(spipe[1]);
472                 return -1;
473         }
474         if (children[0]) {
475                 /********* in the parent process ************/
476                 close(mpipe[1]);
477                 close(spipe[0]);
478                 /* wait the ready signal (that transmit the slave pid) */
479                 rc = read(mpipe[0], &children[1], sizeof children[1]);
480                 close(mpipe[0]);
481                 if (rc  <= 0) {
482                         ERROR("reading master pipe failed: %m");
483                         close(spipe[1]);
484                         return -1;
485                 }
486                 assert(rc == sizeof children[1]);
487                 /* start the child */
488                 rc = write(spipe[1], "start", 5);
489                 if (rc < 0) {
490                         ERROR("writing slave pipe failed: %m");
491                         close(spipe[1]);
492                         return -1;
493                 }
494                 assert(rc == 5);
495                 close(spipe[1]);
496                 return 0;
497         }
498
499         /********* in the master child ************/
500         close(mpipe[0]);
501         close(spipe[1]);
502
503         /* avoid set-gid effect */
504         setresgid(groupid, groupid, groupid);
505
506         /* enter the process group */
507         rc = setpgid(0, 0);
508         if (rc) {
509                 ERROR("setpgid failed");
510                 _exit(1);
511         }
512
513         /* enter security mode */
514         rc = secmgr_prepare_exec(desc->tag);
515         if (rc < 0) {
516                 ERROR("call to secmgr_prepare_exec failed: %m");
517                 _exit(1);
518         }
519
520         /* enter the datadirectory */
521         rc = mkdir(params->datadir, 0755);
522         if (rc && errno != EEXIST) {
523                 ERROR("creation of datadir %s failed: %m", params->datadir);
524                 _exit(1);
525         }
526         rc = chdir(params->datadir);
527         if (rc) {
528                 ERROR("can't enter the datadir %s: %m", params->datadir);
529                 _exit(1);
530         }
531
532         /* fork the slave child */
533         children[1] = fork();
534         if (children[1] < 0) {
535                 ERROR("slave fork failed: %m");
536                 _exit(1);
537         }
538         if (children[1] == 0) {
539                 /********* in the slave child ************/
540                 close(mpipe[0]);
541                 rc = read(spipe[0], message, sizeof message);
542                 if (rc <= 0) {
543                         ERROR("reading slave pipe failed: %m");
544                         _exit(1);
545                 }
546
547                 args = instantiate_arguments(params->slave, desc, params);
548                 if (args == NULL) {
549                         ERROR("out of memory in slave");
550                 }
551                 else {
552                         rc = execve(args[0], args, environ);
553                         ERROR("failed to exec slave %s: %m", args[0]);
554                 }
555                 _exit(1);
556         }
557
558         /********* still in the master child ************/
559         close(spipe[1]);
560         args = instantiate_arguments(params->master, desc, params);
561         if (args == NULL) {
562                 ERROR("out of memory in master");
563         }
564         else {
565                 rc = write(mpipe[1], &children[1], sizeof children[1]);
566                 if (rc <= 0) {
567                         ERROR("can't write master pipe: %m");
568                 }
569                 else {
570                         close(mpipe[1]);
571                         rc = execve(args[0], args, environ);
572                         ERROR("failed to exec master %s: %m", args[0]);
573                 }
574         }
575         _exit(1);
576 }
577
578 int afm_launch_initialize()
579 {
580         int rc;
581         gid_t r, e, s;
582
583         getresgid(&r, &e, &s);
584         if (s && s != e)
585                 groupid = s;
586         else
587                 groupid = -1;
588
589         rc = read_configuration_file(FWK_LAUNCH_CONF);
590         dump_launchers(&launchers);
591         return rc;
592 }
593
594 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
595 {
596         char datadir[PATH_MAX];
597         int ikl, rc;
598         char secret[9];
599         struct launchparam params;
600         const char *type;
601
602         /* should be init */
603         assert(groupid != 0);
604         assert(launch_mode_is_valid(desc->mode));
605
606         /* init */
607         children[0] = 0;
608         children[1] = 0;
609
610         /* what launcher ? */
611         type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
612         ikl = 0;
613         while (ikl < launchers.count && strcmp(type, launchers.descs[ikl].type))
614                 ikl++;
615         if (ikl == launchers.count) {
616                 ERROR("type %s not found!", type);
617                 errno = ENOENT;
618                 return -1;
619         }
620
621         /* prepare paths */
622         rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
623         if (rc < 0 || rc >= sizeof datadir) {
624                 ERROR("overflow for datadir");
625                 errno = EINVAL;
626                 return -1;
627         }
628
629         /* make the secret and port */
630         mksecret(secret);
631         params.port = mkport();
632         params.secret = secret;
633         params.datadir = datadir;
634         params.master = (const char **)launchers.descs[ikl].execs[0];
635         params.slave = (const char **)launchers.descs[ikl].execs[1];
636
637         return params.slave ? launchexec2(desc, children, &params) : launchexec1(desc, children, &params);
638 }
639