12a481ea5d771ff112ca7e0bf35e62000f6ffec5
[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.h"
36 #include "secmgr-wrap.h"
37
38 /*
39 %I icondir                      FWK_ICON_DIR
40 %P port                         params->port
41 %S secret                       params->secret
42 %D datadir                      params->datadir
43 %r rootdir                      desc->path
44 %h homedir                      desc->home
45 %t tag (smack label)            desc->tag
46 %a appid                        desc->appid
47 %c content                      desc->content
48 %m mime-type                    desc->type
49 %n name                         desc->name
50 %p plugins                      desc->plugins
51 %W width                        desc->width
52 %H height                       desc->height
53 */
54
55 static const char *launch_master_args[] = {
56         "/usr/bin/echo",
57         "--alias=/icons:%I",
58         "--port=%P",
59         "--rootdir=%D",
60         "--token=%S",
61         NULL
62 };
63
64 static const char *launch_html_args[] = {
65         "/usr/bin/chromium",
66         "--single-process",
67         "--user-data-dir=%D",
68         "--data-path=%r",
69         "file://%r/%c",
70 /*
71         "http://localhost:%P",
72 */
73         NULL
74 };
75
76 static const char *launch_bin_args[] = {
77         "/usr/bin/echo",
78         "BINARY",
79         NULL
80 };
81
82 static const char *launch_qml_args[] = {
83         "/usr/bin/echo",
84         "QML",
85         NULL
86 };
87
88 static struct {
89         const char *type;
90         const char **launch_args;
91 }
92 known_launchers[] = {
93         { "text/html",                launch_html_args },
94         { "application/x-executable", launch_bin_args },
95         { "application/octet-stream", launch_bin_args },
96         { "text/vnd.qt.qml",          launch_qml_args }
97 };
98
99 struct launchparam {
100         int port;
101         const char *secret;
102         const char *datadir;
103 };
104
105 static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params)
106 {
107         const char **iter, *p, *v;
108         char *data, **result, port[20], width[20], height[20], mini[3], c;
109         int n, s, x;
110
111         /* init */
112         mini[0] = '%';
113         mini[2] = 0;
114
115         /* loop that either compute the size and build the result */
116         n = s = x = 0;
117         for (;;) {
118                 iter = args;
119                 n = 0;
120                 while (*iter) {
121                         p = *iter++;
122                         if (x)
123                                 result[n] = data;
124                         n++;
125                         while((c = *p++) != 0) {
126                                 if (c != '%') {
127                                         if (x)
128                                                 *data++ = c;
129                                         else
130                                                 s++;
131                                 } else {
132                                         c = *p++;
133                                         switch (c) {
134                                         case 'I': v = FWK_ICON_DIR; break;
135                                         case 'P': if(!x) sprintf(port, "%d", params->port); v = port; break;
136                                         case 'S': v = params->secret; break;
137                                         case 'D': v = params->datadir; break;
138                                         case 'r': v = desc->path; break;
139                                         case 'h': v = desc->home; break;
140                                         case 't': v = desc->tag; break;
141                                         case 'a': v = desc->appid; break;
142                                         case 'c': v = desc->content; break;
143                                         case 'm': v = desc->type; break;
144                                         case 'n': v = desc->name; break;
145                                         case 'p': v = "" /*desc->plugins*/; break;
146                                         case 'W': if(!x) sprintf(width, "%d", desc->width); v = width; break;
147                                         case 'H': if(!x) sprintf(height, "%d", desc->height); v = height; break;
148                                         case '%': c = 0;
149                                         default: mini[1] = c; v = mini; break;
150                                         }
151                                         if (x)
152                                                 data = stpcpy(data, v);
153                                         else
154                                                 s += strlen(v);
155                                 }
156                         }
157                         if (x)
158                                 *data++ = 0;
159                         else
160                                 s++;
161                 }
162                 if (x) {
163                         result[n] = NULL;
164                         return result;
165                 }
166                 /* allocation */
167                 result = malloc((n+1)*sizeof(char*) + s);
168                 if (result == NULL) {
169                         errno = ENOMEM;
170                         return NULL;
171                 }
172                 data = (char*)(&result[n + 1]);
173                 x = 1;
174         }
175 }
176
177 static void mksecret(char buffer[9])
178 {
179         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
180 }
181
182 static int mkport()
183 {
184         static int port_ring = 12345;
185         int port = port_ring;
186         if (port < 12345 || port > 15432)
187                 port = 12345;
188         port_ring = port + 1;
189         return port;
190 }
191
192 int afm_launch(struct afm_launch_desc *desc, pid_t children[2])
193 {
194         char datadir[PATH_MAX];
195         int ikl, nkl, rc;
196         char secret[9];
197         int port;
198         char message[10];
199         int mpipe[2];
200         int spipe[2];
201         struct launchparam params;
202         char **args;
203
204         /* what launcher ? */
205         ikl = 0;
206         if (desc->type != NULL && *desc->type) {
207                 nkl = sizeof known_launchers / sizeof * known_launchers;
208                 while (ikl < nkl && strcmp(desc->type, known_launchers[ikl].type))
209                         ikl++;
210                 if (ikl == nkl) {
211                         ERROR("type %s not found!", desc->type);
212                         errno = ENOENT;
213                         return -1;
214                 }
215         }
216
217         /* prepare paths */
218         rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
219         if (rc < 0 || rc >= sizeof datadir) {
220                 ERROR("overflow for datadir");
221                 errno = EINVAL;
222                 return -1;
223         }
224
225         /* make the secret and port */
226         mksecret(secret);
227         port = mkport();
228
229         params.port = port;
230         params.secret = secret;
231         params.datadir = datadir;
232
233         /* prepare the pipes */
234         rc = pipe2(mpipe, O_CLOEXEC);
235         if (rc < 0) {
236                 ERROR("error while calling pipe2: %m");
237                 return -1;
238         }
239         rc = pipe2(spipe, O_CLOEXEC);
240         if (rc < 0) {
241                 ERROR("error while calling pipe2: %m");
242                 close(spipe[0]);
243                 close(spipe[1]);
244                 return -1;
245         }
246
247         /* fork the master child */
248         children[0] = fork();
249         if (children[0] < 0) {
250                 ERROR("master fork failed: %m");
251                 close(mpipe[0]);
252                 close(mpipe[1]);
253                 close(spipe[0]);
254                 close(spipe[1]);
255                 return -1;
256         }
257         if (children[0]) {
258                 /********* in the parent process ************/
259                 close(mpipe[1]);
260                 close(spipe[0]);
261                 /* wait the ready signal (that transmit the slave pid) */
262                 rc = read(mpipe[0], &children[1], sizeof children[1]);
263                 if (rc  < 0) {
264                         ERROR("reading master pipe failed: %m");
265                         close(mpipe[0]);
266                         close(spipe[1]);
267                         return -1;
268                 }
269                 close(mpipe[0]);
270                 assert(rc == sizeof children[1]);
271                 /* start the child */
272                 rc = write(spipe[1], "start", 5);
273                 if (rc < 0) {
274                         ERROR("writing slave pipe failed: %m");
275                         close(spipe[1]);
276                         return -1;
277                 }
278                 assert(rc == 5);
279                 close(spipe[1]);
280                 return 0;
281         }
282
283         /********* in the master child ************/
284         close(mpipe[0]);
285         close(spipe[1]);
286
287         /* enter the process group */
288         rc = setpgid(0, 0);
289         if (rc) {
290                 ERROR("setpgid failed");
291                 _exit(1);
292         }
293
294         /* enter security mode */
295         rc = secmgr_prepare_exec(desc->tag);
296         if (rc < 0) {
297                 ERROR("call to secmgr_prepare_exec failed: %m");
298                 _exit(1);
299         }
300
301         /* enter the datadirectory */
302         rc = mkdir(datadir, 0755);
303         if (rc && errno != EEXIST) {
304                 ERROR("creation of datadir %s failed: %m", datadir);
305                 _exit(1);
306         }
307         rc = chdir(datadir);
308         if (rc) {
309                 ERROR("can't enter the datadir %s: %m", datadir);
310                 _exit(1);
311         }
312
313         /* fork the slave child */
314         children[1] = fork();
315         if (children[1] < 0) {
316                 ERROR("slave fork failed: %m");
317                 _exit(1);
318         }
319         if (children[1] == 0) {
320                 /********* in the slave child ************/
321                 close(mpipe[0]);
322                 rc = read(spipe[0], message, sizeof message);
323                 if (rc < 0) {
324                         ERROR("reading slave pipe failed: %m");
325                         _exit(1);
326                 }
327
328                 args = instantiate_arguments(known_launchers[ikl].launch_args, desc, &params);
329                 if (args == NULL) {
330                         ERROR("out of memory in slave");
331                 }
332                 else {
333                         rc = execve(args[0], args, environ);
334                         ERROR("failed to exec slave %s: %m", args[0]);
335                 }
336                 _exit(1);
337         }
338
339         /********* still in the master child ************/
340         close(spipe[1]);
341         args = instantiate_arguments(launch_master_args, desc, &params);
342         if (args == NULL) {
343                 ERROR("out of memory in master");
344         }
345         else {
346                 rc = write(mpipe[1], &children[1], sizeof children[1]);
347                 if (rc < 0) {
348                         ERROR("can't write master pipe: %m");
349                 }
350                 else {
351                         close(mpipe[1]);
352                         rc = execve(args[0], args, environ);
353                         ERROR("failed to exec master %s: %m", args[0]);
354                 }
355         }
356         _exit(1);
357 }
358