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