Bug fix and authoring
[src/app-framework-main.git] / src / af-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 "af-launch.h"
36 #include "secmgr-wrap.h"
37
38 struct launchparam {
39         int port;
40         const char *secret;
41 };
42
43 static int launch_html(struct af_launch_desc *desc, struct launchparam *params);
44 static int launch_bin(struct af_launch_desc *desc, struct launchparam *params);
45 static int launch_qml(struct af_launch_desc *desc, struct launchparam *params);
46
47 static int launch_master(struct af_launch_desc *desc, struct launchparam *params, int fd, pid_t child);
48
49 static struct {
50         const char *type;
51         int (*launcher)(struct af_launch_desc *desc, struct launchparam *params);
52 }
53 known_launchers[] = {
54         { "text/html", launch_html },
55         { "application/x-executable", launch_bin },
56         { "application/octet-stream", launch_bin },
57         { "text/vnd.qt.qml", launch_qml }
58 };
59
60 static void mksecret(char buffer[9])
61 {
62         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
63 }
64
65 static int mkport()
66 {
67         static int port_ring = 12345;
68         int port = port_ring;
69         if (port < 12345 || port > 15432)
70                 port = 12345;
71         port_ring = port + 1;
72         return port;
73 }
74
75 int af_launch(struct af_launch_desc *desc, pid_t children[2])
76 {
77         char datadir[PATH_MAX];
78         int ikl, nkl, rc;
79         char secret[9];
80         int port;
81         char message[10];
82         int mpipe[2];
83         int spipe[2];
84         struct launchparam params;
85
86         /* what launcher ? */
87         ikl = 0;
88         if (desc->type != NULL && *desc->type) {
89                 nkl = sizeof known_launchers / sizeof * known_launchers;
90                 while (ikl < nkl && strcmp(desc->type, known_launchers[ikl].type))
91                         ikl++;
92                 if (ikl == nkl) {
93                         ERROR("type %s not found!", desc->type);
94                         errno = ENOENT;
95                         return -1;
96                 }
97         }
98
99         /* prepare paths */
100         rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
101         if (rc < 0 || rc >= sizeof datadir) {
102                 ERROR("overflow for datadir");
103                 errno = EINVAL;
104                 return -1;
105         }
106
107         /* make the secret and port */
108         mksecret(secret);
109         port = mkport();
110
111         params.port = port;
112         params.secret = secret;
113
114         /* prepare the pipes */
115         rc = pipe2(mpipe, O_CLOEXEC);
116         if (rc < 0) {
117                 ERROR("error while calling pipe2: %m");
118                 return -1;
119         }
120         rc = pipe2(spipe, O_CLOEXEC);
121         if (rc < 0) {
122                 ERROR("error while calling pipe2: %m");
123                 close(spipe[0]);
124                 close(spipe[1]);
125                 return -1;
126         }
127
128         /* fork the master child */
129         children[0] = fork();
130         if (children[0] < 0) {
131                 ERROR("master fork failed: %m");
132                 close(mpipe[0]);
133                 close(mpipe[1]);
134                 close(spipe[0]);
135                 close(spipe[1]);
136                 return -1;
137         }
138         if (children[0]) {
139                 /********* in the parent process ************/
140                 close(mpipe[1]);
141                 close(spipe[0]);
142                 /* wait the ready signal (that transmit the slave pid) */
143                 rc = read(mpipe[0], &children[1], sizeof children[1]);
144                 if (rc  < 0) {
145                         ERROR("reading master pipe failed: %m");
146                         close(mpipe[0]);
147                         close(spipe[1]);
148                         return -1;
149                 }
150                 close(mpipe[0]);
151                 assert(rc == sizeof children[1]);
152                 /* start the child */
153                 rc = write(spipe[1], "start", 5);
154                 if (rc < 0) {
155                         ERROR("writing slave pipe failed: %m");
156                         close(spipe[1]);
157                         return -1;
158                 }
159                 assert(rc == 5);
160                 close(spipe[1]);
161                 return 0;
162         }
163
164         /********* in the master child ************/
165         close(mpipe[0]);
166         close(spipe[1]);
167
168         /* enter the process group */
169         rc = setpgid(0, 0);
170         if (rc) {
171                 ERROR("setpgid failed");
172                 _exit(1);
173         }
174
175         /* enter security mode */
176         rc = secmgr_prepare_exec(desc->tag);
177         if (rc < 0) {
178                 ERROR("call to secmgr_prepare_exec failed: %m");
179                 _exit(1);
180         }
181
182         /* enter the datadirectory */
183         rc = mkdir(datadir, 0755);
184         if (rc && errno != EEXIST) {
185                 ERROR("creation of datadir %s failed: %m", datadir);
186                 _exit(1);
187         }
188         rc = chdir(datadir);
189         if (rc) {
190                 ERROR("can't enter the datadir %s: %m", datadir);
191                 _exit(1);
192         }
193
194         /* fork the slave child */
195         children[1] = fork();
196         if (children[1] < 0) {
197                 ERROR("slave fork failed: %m");
198                 _exit(1);
199         }
200         if (children[1] == 0) {
201                 /********* in the slave child ************/
202                 close(mpipe[0]);
203                 rc = read(spipe[0], message, sizeof message);
204                 if (rc < 0) {
205                         ERROR("reading slave pipe failed: %m");
206                         _exit(1);
207                 }
208                 rc = known_launchers[ikl].launcher(desc, &params);
209                 ERROR("slave launch failed: %m");
210                 _exit(1);
211         }
212
213         /********* still in the master child ************/
214         close(spipe[1]);
215         rc = launch_master(desc, &params, mpipe[1], children[1]);
216         ERROR("master launch failed: %m");
217         _exit(1);
218 }
219
220 static int launch_master(struct af_launch_desc *desc, struct launchparam *params, int fd, pid_t child)
221 {
222         int rc;
223         char *argv[6];
224         argv[0] = "/usr/bin/echo";
225         (void)asprintf(&argv[1], "--alias=/icons:%s", FWK_ICON_DIR);
226         (void)asprintf(&argv[2], "--port=%d", params->port);
227         (void)asprintf(&argv[3], "--rootdir=%s", desc->path);
228         (void)asprintf(&argv[4], "--token=%s", params->secret);
229         argv[5] = NULL;
230
231         rc = write(fd, &child, sizeof child);
232         if (rc < 0) {
233                 ERROR("can't write master pipe: %m");
234                 return -1;
235         }
236         close(fd);
237         rc = execve(argv[0], argv, environ);
238         ERROR("failed to exec master %s: %m", argv[0]);
239         return rc;
240 }
241
242 static int launch_html(struct af_launch_desc *desc, struct launchparam *params)
243 {
244 /*
245         char *url = asprintf("http://localhost:%d/", params->port);
246 */
247         int rc;
248         char *argv[3];
249         argv[0] = "/usr/bin/chromium";
250         (void)asprintf(&argv[1], "file://%s/%s", desc->path, desc->content);
251         argv[2] = NULL;
252         rc = execve(argv[0], argv, environ);
253         ERROR("failed to exec slave %s: %m", argv[0]);
254         return rc;
255 }
256
257 static int launch_bin(struct af_launch_desc *desc, struct launchparam *params)
258 {
259         ERROR("unimplemented launch_bin");
260         return -1;
261 }
262
263 static int launch_qml(struct af_launch_desc *desc, struct launchparam *params)
264 {
265         ERROR("unimplemented launch_qml");
266         return -1;
267 }
268
269