e085138f70e5a3d0899cfa64f27fc6bfd7b4708e
[src/app-framework-main.git] / src / af-launch.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <limits.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <assert.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29
30 extern char **environ;
31
32 #include "verbose.h"
33 #include "af-launch.h"
34 #include "secmgr-wrap.h"
35
36 struct launchparam {
37         int port;
38         const char *secret;
39 };
40
41 static int launch_html(struct af_launch_desc *desc, struct launchparam *params);
42 static int launch_bin(struct af_launch_desc *desc, struct launchparam *params);
43 static int launch_qml(struct af_launch_desc *desc, struct launchparam *params);
44
45 static int launch_master(struct af_launch_desc *desc, struct launchparam *params, int fd, pid_t child);
46
47 static struct {
48         const char *type;
49         int (*launcher)(struct af_launch_desc *desc, struct launchparam *params);
50 }
51 known_launchers[] = {
52         { "text/html", launch_html },
53         { "application/x-executable", launch_bin },
54         { "application/octet-stream", launch_bin },
55         { "text/vnd.qt.qml", launch_qml }
56 };
57
58 static void mksecret(char buffer[9])
59 {
60         snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
61 }
62
63 static int mkport()
64 {
65         static int port_ring = 12345;
66         int port = port_ring;
67         if (port < 12345 || port > 15432)
68                 port = 12345;
69         port_ring = port + 1;
70         return port;
71 }
72
73 int af_launch(struct af_launch_desc *desc, pid_t children[2])
74 {
75         char datadir[PATH_MAX];
76         int ikl, nkl, rc;
77         char secret[9];
78         int port;
79         char message[10];
80         int mpipe[2];
81         int spipe[2];
82         struct launchparam params;
83
84         /* what launcher ? */
85         ikl = 0;
86         if (desc->type != NULL && *desc->type) {
87                 nkl = sizeof known_launchers / sizeof * known_launchers;
88                 while (ikl < nkl && strcmp(desc->type, known_launchers[ikl].type))
89                         ikl++;
90                 if (ikl == nkl) {
91                         ERROR("type %s not found!", desc->type);
92                         errno = ENOENT;
93                         return -1;
94                 }
95         }
96
97         /* prepare paths */
98         rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
99         if (rc < 0 || rc >= sizeof datadir) {
100                 ERROR("overflow for datadir");
101                 errno = EINVAL;
102                 return -1;
103         }
104
105         /* make the secret and port */
106         mksecret(secret);
107         port = mkport();
108
109         params.port = port;
110         params.secret = secret;
111
112         /* prepare the pipes */
113         rc = pipe2(mpipe, O_CLOEXEC);
114         if (rc < 0) {
115                 ERROR("error while calling pipe2: %m");
116                 return -1;
117         }
118         rc = pipe2(spipe, O_CLOEXEC);
119         if (rc < 0) {
120                 ERROR("error while calling pipe2: %m");
121                 close(spipe[0]);
122                 close(spipe[1]);
123                 return -1;
124         }
125
126         /* fork the master child */
127         children[0] = fork();
128         if (children[0] < 0) {
129                 ERROR("master fork failed: %m");
130                 close(mpipe[0]);
131                 close(mpipe[1]);
132                 close(spipe[0]);
133                 close(spipe[1]);
134                 return -1;
135         }
136         if (children[0]) {
137                 /********* in the parent process ************/
138                 close(mpipe[1]);
139                 close(spipe[0]);
140                 /* wait the ready signal (that transmit the slave pid) */
141                 rc = read(mpipe[0], &children[1], sizeof children[1]);
142                 if (rc  < 0) {
143                         ERROR("reading master pipe failed: %m");
144                         close(mpipe[0]);
145                         close(spipe[1]);
146                         return -1;
147                 }
148                 close(mpipe[0]);
149                 assert(rc == sizeof children[1]);
150                 /* start the child */
151                 rc = write(spipe[1], "start", 5);
152                 if (rc < 0) {
153                         ERROR("writing slave pipe failed: %m");
154                         close(spipe[1]);
155                         return -1;
156                 }
157                 close(spipe[1]);
158                 return 0;
159         }
160
161         /********* in the master child ************/
162         close(mpipe[0]);
163         close(spipe[1]);
164
165         /* enter the process group */
166         rc = setpgid(0, 0);
167         if (rc) {
168                 ERROR("setpgid failed");
169                 _exit(1);
170         }
171
172         /* enter security mode */
173         rc = secmgr_prepare_exec(desc->tag);
174         if (rc < 0) {
175                 ERROR("call to secmgr_prepare_exec failed: %m");
176                 _exit(1);
177         }
178
179         /* enter the datadirectory */
180         rc = mkdir(datadir, 0755);
181         if (rc && errno != EEXIST) {
182                 ERROR("creation of datadir %s failed: %m", datadir);
183                 _exit(1);
184         }
185         rc = chdir(datadir);
186         if (rc) {
187                 ERROR("can't enter the datadir %s: %m", datadir);
188                 _exit(1);
189         }
190
191         /* fork the slave child */
192         children[1] = fork();
193         if (children[1] < 0) {
194                 ERROR("slave fork failed: %m");
195                 _exit(1);
196         }
197         if (children[1] == 0) {
198                 /********* in the slave child ************/
199                 close(mpipe[0]);
200                 rc = read(spipe[0], message, sizeof message);
201                 if (rc < 0) {
202                         ERROR("reading slave pipe failed: %m");
203                         _exit(1);
204                 }
205                 rc = known_launchers[ikl].launcher(desc, &params);
206                 ERROR("slave launch failed: %m");
207                 _exit(1);
208         }
209
210         /********* still in the master child ************/
211         close(spipe[1]);
212         rc = launch_master(desc, &params, mpipe[1], children[1]);
213         ERROR("master launch failed: %m");
214         _exit(1);
215 }
216
217 static int launch_master(struct af_launch_desc *desc, struct launchparam *params, int fd, pid_t child)
218 {
219         int rc;
220         char *argv[6];
221         argv[0] = "/bin/echo";
222         (void)asprintf(&argv[1], "--alias=/icons:%s", FWK_ICON_DIR);
223         (void)asprintf(&argv[2], "--port=%d", params->port);
224         (void)asprintf(&argv[3], "--rootdir=%s", desc->path);
225         (void)asprintf(&argv[4], "--token=%", desc->path);
226         argv[5] = NULL;
227
228         rc = write(fd, &child, sizeof child);
229         if (rc < 0) {
230                 ERROR("can't write master pipe: %m");
231                 return -1;
232         }
233
234         rc = execve(argv[0], argv, environ);
235         ERROR("failed to exec master %s: %m", argv[0]);
236         return rc;
237 }
238
239 static int launch_html(struct af_launch_desc *desc, struct launchparam *params)
240 {
241 /*
242         char *url = asprintf("http://localhost:%d/", params->port);
243 */
244         int rc;
245         char *argv[3];
246         argv[0] = "/usr/bin/chromium";
247         (void)asprintf(&argv[1], "file://%s/%s", desc->path, desc->content);
248         argv[2] = NULL;
249         rc = execve(argv[0], argv, environ);
250         ERROR("failed to exec slave %s: %m", argv[0]);
251         return rc;
252 }
253
254 static int launch_bin(struct af_launch_desc *desc, struct launchparam *params)
255 {
256         ERROR("unimplemented launch_bin");
257         return -1;
258 }
259
260 static int launch_qml(struct af_launch_desc *desc, struct launchparam *params)
261 {
262         ERROR("unimplemented launch_qml");
263         return -1;
264 }
265
266