82a3ed2244a719139cbbbfc97300ffef004e8302
[src/app-framework-main.git] / src / af-run.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 #include <fcntl.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <pwd.h>
21 #include <sys/types.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <string.h>
27
28 #include <json.h>
29
30 #include "verbose.h"
31 #include "utils-dir.h"
32 #include "af-launch.h"
33
34 enum appstate {
35         as_starting,
36         as_running,
37         as_stopped,
38         as_terminating,
39         as_terminated
40 };
41
42 struct apprun {
43         struct apprun *next_by_runid;
44         struct apprun *next_by_pgid;
45         int runid;
46         pid_t pids[2]; /* 0: group leader, 1: slave (appli) */
47         enum appstate state;
48         json_object *appli;
49 };
50
51 #define ROOT_RUNNERS_COUNT  32
52 #define MAX_RUNNER_COUNT    32767
53
54 static struct apprun *runners_by_runid[ROOT_RUNNERS_COUNT];
55 static struct apprun *runners_by_pgid[ROOT_RUNNERS_COUNT];
56 static int runnercount = 0;
57 static int runnerid = 0;
58
59 static const char fwk_user_app_dir[] = FWK_USER_APP_DIR;
60 static char *homeappdir;
61
62 /****************** manages pgids **********************/
63
64 /* get a runner by its pgid */
65 static struct apprun *runner_of_pgid(pid_t pgid)
66 {
67         struct apprun *result = runners_by_pgid[(int)(pgid & (ROOT_RUNNERS_COUNT - 1))];
68         while (result && result->pids[0] != pgid)
69                 result = result->next_by_pgid;
70         return result;
71 }
72
73 /* insert a runner for its pgid */
74 static void pgid_insert(struct apprun *runner)
75 {
76         struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))];
77         runner->next_by_pgid = *prev;
78         *prev = runner;
79 }
80
81 /* remove a runner for its pgid */
82 static void pgid_remove(struct apprun *runner)
83 {
84         struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))];
85         runner->next_by_pgid = *prev;
86         *prev = runner;
87 }
88
89 /****************** manages pids **********************/
90
91 /* get a runner by its pid */
92 static struct apprun *runner_of_pid(pid_t pid)
93 {
94         /* try avoiding system call */
95         struct apprun *result = runner_of_pgid(pid);
96         if (result == NULL) {
97                 result = runner_of_pgid(getpgid(pid));
98                 if (result && result->pids[1] != pid)
99                         result = NULL;
100         }
101         return result;
102 }
103
104 /****************** manages runners (by runid) **********************/
105
106 /* get a runner by its runid */
107 static struct apprun *getrunner(int runid)
108 {
109         struct apprun *result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)];
110         while (result && result->runid != runid)
111                 result = result->next_by_runid;
112         return result;
113 }
114
115 /* free an existing runner */
116 static void freerunner(struct apprun *runner)
117 {
118         struct apprun **prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)];
119         assert(*prev);
120         while(*prev != runner) {
121                 prev = &(*prev)->next_by_runid;
122                 assert(*prev);
123         }
124         *prev = runner->next_by_runid;
125         json_object_put(runner->appli);
126         free(runner);
127         runnercount--;
128 }
129
130 /* create a new runner */
131 static struct apprun *createrunner(json_object *appli)
132 {
133         struct apprun *result;
134         struct apprun **prev;
135
136         if (runnercount >= MAX_RUNNER_COUNT) {
137                 errno = EAGAIN;
138                 return NULL;
139         }
140         do {
141                 runnerid++;
142                 if (runnerid > MAX_RUNNER_COUNT)
143                         runnerid = 1;
144         } while(getrunner(runnerid));
145         result = calloc(1, sizeof * result);
146         if (result == NULL)
147                 errno = ENOMEM;
148         else {
149                 prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)];
150                 result->next_by_runid = *prev;
151                 result->next_by_pgid = NULL;
152                 result->runid = runnerid;
153                 result->pids[0] = result->pids[1] = 0;
154                 result->state = as_starting;
155                 result->appli = json_object_get(appli);
156                 *prev = result;
157                 runnercount++;
158         }
159         return result;
160 }
161
162 /**************** signaling ************************/
163 #if 0
164 static void started(int runid)
165 {
166 }
167
168 static void stopped(int runid)
169 {
170 }
171
172 static void continued(int runid)
173 {
174 }
175
176 static void terminated(int runid)
177 {
178 }
179
180 static void removed(int runid)
181 {
182 }
183 #endif
184 /**************** running ************************/
185
186 static int killrunner(int runid, int sig, enum appstate tostate)
187 {
188         int rc;
189         struct apprun *runner = getrunner(runid);
190         if (runner == NULL) {
191                 errno = ENOENT;
192                 rc = -1;
193         }
194         else if (runner->state != as_running && runner->state != as_stopped) {
195                 errno = EPERM;
196                 rc = -1;
197         }
198         else if (runner->state == tostate) {
199                 rc = 0;
200         }
201         else {
202                 rc = killpg(runner->pids[0], sig);
203                 if (!rc)
204                         runner->state = tostate;
205         }
206         return rc;
207 }
208
209 static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
210 {
211         struct apprun *runner;
212
213         runner = runner_of_pgid(info->si_pid);
214         if (!runner)
215                 return;
216
217         switch(info->si_code) {
218         case CLD_EXITED:
219         case CLD_KILLED:
220         case CLD_DUMPED:
221         case CLD_TRAPPED:
222                 runner->state = as_terminated;
223                 pgid_remove(runner);
224                 break;
225
226         case CLD_STOPPED:
227                 runner->state = as_stopped;
228                 break;
229
230         case CLD_CONTINUED:
231                 runner->state = as_running;
232                 break;
233         }
234 }
235
236 /**************** handle af_launch_desc *********************/
237
238 static int get_jstr(struct json_object *obj, const char *key, const char **value)
239 {
240         json_object *data;
241         return json_object_object_get_ex(obj, key, &data)
242                 && json_object_get_type(data) == json_type_string
243                 && (*value = json_object_get_string(data)) != NULL;
244 }
245
246 static int get_jint(struct json_object *obj, const char *key, int *value)
247 {
248         json_object *data;
249         return json_object_object_get_ex(obj, key, &data)
250                 && json_object_get_type(data) == json_type_int
251                 && ((*value = (int)json_object_get_int(data)), 1);
252 }
253
254 static int fill_launch_desc(struct json_object *appli, struct af_launch_desc *desc)
255 {
256         json_object *pub;
257
258         /* main items */
259         if(!json_object_object_get_ex(appli, "public", &pub)
260         || !get_jstr(appli, "path", &desc->path)
261         || !get_jstr(appli, "id", &desc->tag)
262         || !get_jstr(appli, "content", &desc->content)
263         || !get_jstr(appli, "type", &desc->type)
264         || !get_jstr(pub, "name", &desc->name)
265         || !get_jint(pub, "width", &desc->width)
266         || !get_jint(pub, "height", &desc->height)) {
267                 errno = EINVAL;
268                 return -1;
269         }
270
271         /* plugins */
272         {
273                 /* TODO */
274                 static const char *null = NULL;
275                 desc->plugins = &null;
276         }
277
278         /* finaly */
279         desc->home = homeappdir;
280         return 0;
281 };
282
283 /**************** API handling ************************/
284
285 int af_run_start(struct json_object *appli)
286 {
287         static struct apprun *runner;
288         struct af_launch_desc desc;
289         int rc;
290         sigset_t saved, blocked;
291
292         /* prepare to launch */
293         rc = fill_launch_desc(appli, &desc);
294         if (rc)
295                 return rc;
296         runner = createrunner(appli);
297         if (!runner)
298                 return -1;
299
300         /* block children signals until launched */
301         sigemptyset(&blocked);
302         sigaddset(&blocked, SIGCHLD);
303         sigprocmask(SIG_BLOCK, &blocked, &saved);
304
305         /* launch now */
306         rc = af_launch(&desc, runner->pids);
307         if (rc < 0) {
308                 /* fork failed */
309                 sigprocmask(SIG_SETMASK, &saved, NULL);
310                 ERROR("can't start, af_launch failed: %m");
311                 freerunner(runner);
312                 return -1;
313         }
314
315         /* insert the pid */
316         runner->state = as_running;
317         pgid_insert(runner);
318
319         /* unblock children signal now */
320         sigprocmask(SIG_SETMASK, &saved, NULL);
321         return 0;
322 }
323
324 int af_run_terminate(int runid)
325 {
326         return killrunner(runid, SIGTERM, as_terminating);
327 }
328
329 int af_run_stop(int runid)
330 {
331         return killrunner(runid, SIGSTOP, as_stopped);
332 }
333
334 int af_run_continue(int runid)
335 {
336         return killrunner(runid, SIGCONT, as_running);
337 }
338
339 static json_object *mkstate(struct apprun *runner, const char **runidstr)
340 {
341         const char *state;
342         struct json_object *result, *obj, *runid;
343         int rc;
344
345         /* the structure */
346         result = json_object_new_object();
347         if (result == NULL)
348                 goto error;
349
350         /* the runid */
351         runid = json_object_new_int(runner->runid);
352         if (runid == NULL)
353                 goto error2;
354         json_object_object_add(result, "runid", obj); /* TODO TEST STATUS */
355
356         /* the state */
357         switch(runner->state) {
358         case as_starting:
359         case as_running:
360                 state = "running";
361                 break;
362         case as_stopped:
363                 state = "stopped";
364                 break;
365         default:
366                 state = "terminated";
367                 break;
368         }
369         obj = json_object_new_string(state);
370         if (obj == NULL)
371                 goto error2;
372         json_object_object_add(result, "state", obj); /* TODO TEST STATUS */
373
374         /* the application id */
375         rc = json_object_object_get_ex(runner->appli, "public", &obj);
376         assert(rc);
377         rc = json_object_object_get_ex(obj, "id", &obj);
378         assert(rc);
379         json_object_object_add(result, "id", obj); /* TODO TEST STATUS */
380         json_object_get(obj);
381
382         /* done */
383         if (runidstr)
384                 *runidstr = json_object_get_string(runid);
385         return result;
386
387 error2:
388         json_object_put(result);
389 error:
390         errno = ENOMEM;
391         return NULL;
392 }
393
394 struct json_object *af_run_list()
395 {
396         struct json_object *result, *obj;
397         struct apprun *runner;
398         const char *runidstr;
399         int i;
400
401         /* creates the object */
402         result = json_object_new_object();
403         if (result == NULL) {
404                 errno = ENOMEM;
405                 return NULL;            
406         }
407
408         for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
409                 for (runner = runners_by_runid[i] ; runner ; runner = runner->next_by_runid) {
410                         if (runner->state != as_terminating && runner->state != as_terminated) {
411                                 obj = mkstate(runner, &runidstr);
412                                 if (obj == NULL) {
413                                         json_object_put(result);
414                                         return NULL;
415                                 }
416                                 /* TODO status ? */
417                                 json_object_object_add(result, runidstr, obj);
418                         }
419                 }
420         }
421         return result;
422 }
423
424 struct json_object *af_run_state(int runid)
425 {
426         struct apprun *runner = getrunner(runid);
427         if (runner == NULL || runner->state == as_terminating || runner->state == as_terminated) {
428                 errno = ENOENT;
429                 return NULL;
430         }
431         return mkstate(runner, NULL);
432 }
433
434 /**************** INITIALISATION **********************/
435
436 int af_run_init()
437 {
438         char buf[2048];
439         char dir[PATH_MAX];
440         int rc;
441         uid_t me;
442         struct passwd passwd, *pw;
443         struct sigaction siga;
444
445         /* computes the 'homeappdir' */
446         me = geteuid();
447         rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
448         if (rc || pw == NULL) {
449                 errno = rc ? errno : ENOENT;
450                 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
451                 return -1;
452         }
453         rc = snprintf(dir, sizeof dir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
454         if (rc >= sizeof dir) {
455                 ERROR("buffer overflow in user_app_dir for uid=%d",(int)me);
456                 return -1;
457         }
458         rc = create_directory(dir, 0755, 1);
459         if (rc && errno != EEXIST) {
460                 ERROR("creation of directory %s failed in user_app_dir: %m", dir);
461                 return -1;
462         }
463         homeappdir = strdup(dir);
464         if (homeappdir == NULL) {
465                 errno = ENOMEM;
466                 ERROR("out of memory in user_app_dir for %s : %m", dir);
467                 return -1;
468         }
469
470         /* install signal handlers */
471         siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
472         sigemptyset(&siga.sa_mask);
473         sigaddset(&siga.sa_mask, SIGCHLD);
474         siga.sa_sigaction = on_sigchld;
475         sigaction(SIGCHLD, &siga, NULL);
476         return 0;
477 }
478