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