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