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