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