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